Compare commits

...

260 Commits

Author SHA1 Message Date
Chris Sulmone 524c308844 Add Bitcoin Private Support 2018-03-12 01:42:37 -05:00
Roman Zeyde ab5e4eee34 tests: add test_msg_getecdhsessionkey.py (#233) 2018-03-11 22:51:14 +01:00
matejcik 6b51228090 use binascii.hexlify instead of bytearray.hex (which doesn't exist in python < 3.5) 2018-03-09 14:38:04 +01:00
matejcik 5edcea9ba6
transport: fix all_transports when required modules are missing (#232)
This lets the library work without libusb or hidapi (`--disable-libusb`, `--disable-hidapi`).
2018-03-09 10:58:24 +01:00
Tomas Susanka 89eac8f157 tests/device: more Bitcoin Gold tests
including segwit and multisig
updates https://github.com/trezor/trezor-core/issues/147
2018-03-08 14:35:56 +01:00
Tomas Susanka 869af4498b tests/device: Bitcoin Gold tests 2018-03-07 16:43:01 +01:00
Pavol Rusnak 683f383e90
tests: fix test_msg_getpublickey_curve.py (don't try public derivation for ed25519) 2018-03-07 15:46:09 +01:00
matejcik d8c9c970f5 flake8: clean up imports from last commit 2018-03-07 15:28:35 +01:00
matejcik dd052d07b0 better getch() functions, fixed windows version (fixes #207) 2018-03-07 15:18:32 +01:00
matejcik c0d2af557c further cleanup of normalize_nfc usage 2018-03-06 15:50:54 +01:00
matejcik a1dba05a46 travis: do not test python2 2018-03-06 15:50:54 +01:00
matejcik 2c15a861dc replace removed iterbytes with direct iteration 2018-03-06 15:50:54 +01:00
matejcik 5422c40451 start dropping py2 compatibility code 2018-03-06 15:50:54 +01:00
matejcik e1e419485f
Merge pull request #226 from matejcik/refactor-transport-nicediff
prefix search for `trezorctl -p`
2018-03-06 15:50:05 +01:00
matejcik 9f2583f893 webusb: check if a device is functional when enumerating
this fixes issue #223 on Windows, where a device would be returned in two copies, only one of which works
2018-03-06 13:32:51 +01:00
matejcik f75b90d260 Merge branch 'upstream' into refactor-transport-nicediff 2018-03-05 19:56:47 +01:00
matejcik 2752e6d046 bump version to 0.9.1 2018-03-05 19:14:04 +01:00
matejcik 43c71ca8e0 add changelog 2018-03-05 19:11:45 +01:00
matejcik f63b34dbea setup.py: add markers specifying Python 3 only compatibility 2018-03-05 19:11:16 +01:00
matejcik cd9bd06163 prettyprint: fix flake8 complaints 2018-03-05 19:10:54 +01:00
matejcik 52c2319822 omit Features message from debug dumps 2018-03-05 18:57:40 +01:00
matejcik db3767f7ef tweak prettyprint for some known elements 2018-03-05 18:57:40 +01:00
matejcik 07ceb9aacc pretty printing protobufs 2018-03-05 18:57:40 +01:00
Jan Pochyla 95603b85dd tests: enable segwit sign&verify tests for t2 2018-03-05 17:45:17 +01:00
Jan Pochyla 4979c296b7 tests: enable test_apply_settings, test_apply_settings_passphrase for t2 2018-03-05 17:45:17 +01:00
Pavol Rusnak db827bfe01
tests: update test_msg_getpublickey.py, add test_msg_getpublickey_curve.py 2018-03-05 17:37:56 +01:00
matejcik dc8eec1656 trezorlib/transport: for get_transport(None), raise exception from None if no trezor is found,
because the IndexError should not be part of the traceback
2018-03-05 17:31:11 +01:00
matejcik 967d479a19 update tools to use the new transport API
also drop some python2 compatibility things
2018-03-05 17:30:44 +01:00
matejcik ff80ca1b82 restore device.py as a deprecated compatibility wrapper 2018-03-05 16:18:32 +01:00
Jan Pochyla 0e065237c9 tests: disable TestBip32Speed::test_cache on t2 2018-03-05 15:49:04 +01:00
matejcik 2f1c15b588 trezorlib/transport: make flake8 happy 2018-03-05 14:25:37 +01:00
Jochen Hoenicke 8a62d12217 Update ethereum_sign_tx to python3 (#227) 2018-03-05 13:09:57 +01:00
matejcik 513e6aae08 better way for test suite to search for the right device,
that also respects TREZOR_PATH
2018-03-02 18:25:39 +01:00
matejcik 2a706a751a update trezorctl to use prefix search correctly
first, try exact path
second, try prefix search
last, fail :) with reporting used path (for people like me who forget to unset TREZOR_PATH
2018-03-02 18:24:05 +01:00
matejcik 6519657808 trezorlib/transport: smarter handling of prefix search
For UDP transport, it's useful to be able to specify a path that should be tried directly,
without enumerating first.
2018-03-02 18:22:33 +01:00
matejcik 55641dd8b5 make flake8 happy (#225) 2018-03-02 16:47:29 +01:00
matejcik d2913c20bd trezorlib/transport: move TrezorDevice functionality to transport and make it better ^_^ 2018-03-02 16:46:10 +01:00
matejcik 49790d7bfe install the new trasport subpackage 2018-03-02 16:43:41 +01:00
Tomas Susanka b24550c72f tests: ethereum sign/verify is skipped (#224) 2018-03-02 16:37:34 +01:00
matejcik bc8120230a trezorlib/transport: make changes to support being a separate submodule, move common functions to superclass 2018-03-02 15:44:24 +01:00
matejcik 473ea19570 trezorlib/transport: rename files as separate step (to make diffs nicer) 2018-03-02 15:35:56 +01:00
slush e37e9bfebd Fixing Origin header for Bridge 2.0.7 2018-03-01 10:33:47 +01:00
Pavol Rusnak fd41db8a59
trezorctl: don't always require internal entropy in reset_device 2018-03-01 05:07:27 +01:00
Pavol Rusnak b1a76e4a68
tests: revert basic tests 2018-03-01 00:07:27 +01:00
Pavol Rusnak 7841bbefbe
client: implement PassphraseStateRequest handling 2018-02-28 23:13:17 +01:00
Pavol Rusnak ce9da28a3d
update protobuf 2018-02-28 23:13:17 +01:00
matejcik 8404bef6e3 support TREZOR_PATH environment variable for selecting default path (#221) 2018-02-28 18:04:33 +01:00
matejcik 2d3e890c6b setup: add missing requirement for rlp (#220) 2018-02-28 18:03:27 +01:00
Pavol Rusnak 0d680944a4
txcache: add new tx 2018-02-28 00:56:55 +01:00
Pavol Rusnak 4cfcd93d48
transport: force V1 protocol for T2 for now 2018-02-27 18:30:09 +01:00
Tomas Susanka 6e1eb8e664 tests/device: ethereum erc20 tokens test 2018-02-27 17:41:18 +01:00
Jan Pochyla 6c8ccc0680 tests/device_tests: for t2, enable bch & multisig, disable load & reset 2018-02-27 16:29:59 +01:00
Pavol Rusnak a352f41f07
protob: update PassphraseAck 2018-02-27 15:41:59 +01:00
Tomas Susanka a8d34430a5 tests/device/bcash: fix attack amount test 2018-02-27 15:10:36 +01:00
Tomas Susanka aec8f04f68 tests/device: assert exception type and message 2018-02-27 15:08:00 +01:00
Pavol Rusnak 03b3ef10f4
update protobuf 2018-02-27 14:24:23 +01:00
Pavol Rusnak 8dffdd8f85
tests: fix test_basic (don't compare state in Features), add test_basic_state 2018-02-27 14:24:23 +01:00
Tomas Susanka 55da3d9a9a tests/device/signtx: assert exception type and message 2018-02-27 11:50:10 +01:00
Tomas Susanka 617ccc21d9 tests: deepcopy is required when debug_processor is invoked 2018-02-23 16:03:43 +01:00
Tomas Susanka cbd3751bdb tests/device: allow multiple output changes (treat second as a normal output)
based upon e716f7e84e
2018-02-23 13:14:10 +01:00
Tomas Susanka 0e2d5c8155 tests/device: change on main chain IS allowed
based upon e716f7e84e
2018-02-23 13:10:55 +01:00
Tomas Susanka 1e8f2d1e72 tests/device: change on main chain not allowed 2018-02-23 13:09:49 +01:00
Tomas Susanka d6f0c54d3e tests: CallException relaced with AssertionException in response validation
Generic exceptions are evil. When throwing a CallException in the
request check you can't distinguish in the tests if it is an Exception
that occured in during the execution (that's CallException) or during
some tests (that used to be CallException as well).
2018-02-22 16:51:34 +01:00
Tomas Susanka bc036bc857 tests/device: two output changes yield error
this test should fail on T1 after e716f7e84ecdb4732ad97b56e78d7407ec0b321e
2018-02-22 14:20:53 +01:00
Pavol Rusnak cb47dbd284
chmod +x tools/mem_flashblock.py 2018-02-21 16:48:40 +01:00
Pavol Rusnak a0c85bed12
tools: add pwd_reader from SLIP-0016 2018-02-21 16:46:18 +01:00
Jan Pochyla 2c91a668aa tests: fix udp debuglink 2018-02-21 15:31:32 +01:00
Pavol Rusnak bccd61cb23
client: implement PassphraseRequest.on_device handling 2018-02-14 19:11:21 +01:00
Pavol Rusnak e256281a99
rebuild protobuf to add {Initialize,Features}.state 2018-02-09 17:46:54 +01:00
Tomas Susanka 30e5c80956 Multisig tests enabled for t2 (#216) 2018-02-08 15:36:26 +01:00
脇山P 4cbf74f789 Support monacoin (#215) 2018-02-08 14:09:40 +01:00
Pavol Rusnak 1c3b05a44b
bump version to 0.9.0 2018-02-06 22:12:17 +01:00
Pavol Rusnak ba8bb99097
fix flake8 error 2018-02-06 21:39:02 +01:00
slush 489b1eb074 Removing unused transport_pipe.py 2018-02-06 21:30:13 +01:00
slush e141a6f5d1 Added get_path() to transports 2018-02-06 21:10:30 +01:00
slush f00a689087 Remove unnecessary logging 2018-02-06 20:52:45 +01:00
slush 6a22cf481c Fix handling of find_by_path in transports. 2018-02-06 18:40:07 +01:00
Pavol Rusnak d45cba1ddb
messages: add fw_vendor_keys to Features message 2018-02-06 16:25:30 +01:00
Pavol Rusnak 272ad30898
messages: add new fields to Features 2018-02-06 16:07:41 +01:00
Pavol Rusnak e9705c8208
webusb: don't create usb context on WebUsbTransport import 2018-02-04 12:05:03 +01:00
Roman Zeyde 1b6873eb20 Allow compatibility with Python 2 (#214)
Following https://github.com/romanz/trezor-agent/issues/195
2018-02-04 11:44:20 +01:00
Yash 9ec331ed46 Add in import for TransportException in transport_udp.py (#212) 2018-02-04 11:42:58 +01:00
slush 9ebe1b5204 Remove debug print 2018-02-02 20:18:30 +01:00
slush 81db1da68f Fix handling of bytes/str in transport paths 2018-02-02 20:17:10 +01:00
Karel Bilek 29ad78d57b Ignoring non-webusb devices 2018-02-02 19:30:11 +01:00
slush ac09c8d7de Make all transport prefixes lowercase. 2018-02-02 19:20:03 +01:00
slush 03a11450c1 Adding bridge transport to TrezorDevice, using as default transport 2018-02-02 19:17:48 +01:00
slush 562a19c812 Make examples working for all available transports. 2018-02-02 18:29:52 +01:00
slush a4cdae39af Introducing TrezorDevice, removing concept of transports from trezorctl 2018-02-02 18:29:20 +01:00
slush fae11f2996 Use python3 for setup 2018-02-02 16:36:29 +01:00
slush b32b59cc51 Use python3 in README 2018-02-02 16:34:30 +01:00
Pavol Rusnak 1a046b524c
setup: add dependency for libusb1 2018-02-02 01:22:16 +01:00
Pavol Rusnak 646338c414
small nits of last commit 2018-02-01 10:31:47 +01:00
Karel Bilek 759316e96f Add webusb to transports
V2 protocol with debug link is not tested.
2018-02-01 10:25:01 +01:00
Pavol Rusnak 7b844f0379
add Sint64 to protobuf 2018-01-30 15:04:24 +01:00
slush 11fd72890c trezorctl: Do not display PASSPHRASE on screen. 2018-01-29 18:09:42 +01:00
slush ab42e93718 trezorctl: Allow entering passphrase by environment variable PASSPHRASE. 2018-01-29 18:04:48 +01:00
Pavol Rusnak 5b3e992521
transport: update to new bridge API 2018-01-29 17:48:08 +01:00
slush 87cd375b35 Fixing string conversion in encrypt_keyvalue/decrypt_keyvalue. 2018-01-26 05:09:08 +01:00
Anton Kolesnyk a8cd90c3ad Add varying url to broadcast tx, depending on the api 2018-01-15 00:25:05 +01:00
Anton Kolesnyk 28c9820b3d Fixes for BlockCypher API and Dogecoin. 2018-01-14 16:14:39 +01:00
Pavol Rusnak c78c548752
protobuf: delete old messages first 2018-01-12 13:10:36 +01:00
Pavol Rusnak 694bc7ac11
protobuf: don't generate storage protobuf 2018-01-12 13:06:43 +01:00
Pavol Rusnak 8f6b2449be
sort imports in pb2py 2018-01-12 12:58:39 +01:00
Pavol Rusnak 78d2c07d34
regenerate pb messages 2018-01-12 12:54:06 +01:00
Pavol Rusnak 47cfa178e4
tests: fix test_msg_ethereum_signtx.py 2018-01-11 23:06:47 +01:00
Tomas Susanka 0c517c1565 tests/device: code style typos 2018-01-11 22:54:08 +01:00
Tomas Susanka 4bbf5880ce tests/device: ethereum sign tx with expected checks 2018-01-11 22:54:08 +01:00
Pavol Rusnak e618402429
trezorctl: firmware_update -e erases firmware (first 32K; rendering it unusable) 2018-01-09 12:12:32 +01:00
Pavol Rusnak 0c5eac2f39
skip None and empty ([]) fields in proto messages 2018-01-07 18:07:13 +01:00
Pavol Rusnak f587135b8d
fix last commit (also skip print statement) 2018-01-04 16:55:27 +01:00
Tomas Susanka 4e01971e4c client: expected field check fix 2018-01-04 16:48:16 +01:00
slush c71f234a8b Added deprecation warning for Python2
Removed dependency to google's protobuf in bridge transport
Fixed PinRequest handling
2017-12-29 19:19:18 +01:00
Pavol Rusnak 6b31ac9753
fix typo 2017-12-27 01:44:26 +01:00
Anton Kolesnyk 956d5e7149 Add sign_tx support for Dogecoin 2017-12-27 01:39:06 +01:00
mcudev 4962207703
sign_tx: add rbf opt-in enable, add locktime, add tx version 2017-12-27 01:28:59 +01:00
Saleem Rashid 79da872316 trezorctl: Guess script type from BIP-32 in sign_tx
Also add change output to sign_tx
2017-12-27 01:16:46 +01:00
Saleem Rashid 881015ae5f trezorctl: Ask for input script type in sign_tx 2017-12-27 01:16:46 +01:00
Saleem Rashid 35db3c5efb trezorctl: Add ChoiceType to replace click.Choice 2017-12-27 01:16:46 +01:00
Pavol Rusnak 888b6f9171
fix file flags 2017-12-24 22:37:24 +01:00
Martin Skoviera dac97ed5b6 Fixed enums in WordRequestType 2017-12-24 22:37:01 +01:00
Pavol Rusnak d94b68fd30
fix flake8 warning 2017-12-23 22:13:09 +01:00
Pavol Rusnak 70e6d13c23
device tests: simplify, drop unittest dependency 2017-12-23 22:03:24 +01:00
Pavol Rusnak 1881b0e6fd
device tests: re-enable ethereum tests for T2 again 2017-12-23 13:51:18 +01:00
Roman Zeyde 31c4836073 udp: fix __str__ method and allow simple enumeration 2017-12-23 13:43:51 +01:00
Roman Zeyde f8a277dfba transport_bridge: fix messages' module import 2017-12-23 13:43:51 +01:00
Roman Zeyde 8689440d90 client: fix PinMatrixRequestType enum usage 2017-12-23 13:43:51 +01:00
Saleem Rashid b3ef649f64 device_tests: Add test_decred_multisig_change 2017-12-23 13:42:59 +01:00
Saleem Rashid 2df19127fd device_tests: Add test_decred_send_change 2017-12-23 13:42:59 +01:00
Saleem Rashid 0926ab9bc8 device_tests: Clean up test_decred_send 2017-12-23 13:42:59 +01:00
Pavol Rusnak 41b75c5655
device_tests: use skip_t1 and skip_t2 markers 2017-12-19 19:24:40 +01:00
Saleem Rashid 2c00526d23 client: Remove DEFAULT_CURVE
The device should choose the default curve based on the coin or message.
2017-12-19 18:47:18 +01:00
Pavol Rusnak c550e5c703
revert bytes/str change in tools.py 2017-12-19 16:10:37 +01:00
Pavol Rusnak 8a37c28ed6
fix typos in test names 2017-12-19 15:54:07 +01:00
Saleem Rashid f20fd0d8cf trezorctl: Remove broken default in address_n click.prompt 2017-12-19 15:48:17 +01:00
Saleem Rashid 36c479c2c2 trezorctl: Change InputScriptType to OutputScriptType 2017-12-19 15:48:17 +01:00
Saleem Rashid fcad6d0e28 tox: Run trezorlib.tests.unit_tests 2017-12-19 13:16:22 +01:00
Saleem Rashid 2996138341 protobuf: Call _fill_missing in __init__ 2017-12-19 13:16:22 +01:00
Saleem Rashid 1c8f03968c tests: Move to trezorlib.tests 2017-12-19 13:16:22 +01:00
Saleem Rashid 57ad0fe729 unit_tests: Fix tx_api.cache_dir 2017-12-19 13:16:22 +01:00
Saleem Rashid 34a8b90067 device_tests: Fix tx_api.cache_dir 2017-12-19 13:16:22 +01:00
Pavol Rusnak 753e91dff0
protobuf: encode to utf-8 bytestream 2017-12-18 22:44:54 +01:00
Pavol Rusnak 094d0b6ffb
revert ckd_public.py removal of bytes/string handling 2017-12-18 22:40:11 +01:00
Pavol Rusnak c1b1bedb8c
ed25519: remove py2/py3 handling in ed25519 funcs as well 2017-12-18 22:34:15 +01:00
Pavol Rusnak a9291e89c5
no need to use byteindex/iterbytes anymore 2017-12-18 22:26:55 +01:00
Saleem Rashid f2a52400c3 device_tests: Round time in test_backoff
Fix random failures on emulator due to minimal communication overhead
2017-12-18 21:19:22 +01:00
Saleem Rashid fd32c3aa84 device_tests: Fix test_protect_call 2017-12-18 21:19:22 +01:00
Saleem Rashid 90c49e3386 setup: Use packages instead of py_modules
Fixes #176
2017-12-18 19:30:17 +01:00
Saleem Rashid feec0a572c device_tests: Add TestMsgSigntxDecred 2017-12-18 16:34:43 +01:00
Saleem Rashid 9229f8b80a coins: Add Decred Testnet 2017-12-18 16:34:43 +01:00
Saleem Rashid d446e56375 trezorctl: Fix sign_tx default BIP-32 path 2017-12-17 22:12:57 +01:00
Saleem Rashid 496bfc74fd trezorctl: Refactor sign_tx to use click.prompt
Fixes UnboundLocalError on Python 3
2017-12-17 22:12:57 +01:00
Saleem Rashid c48724eca6 client: Fix string encoding for Python 2 2017-12-17 22:10:40 +01:00
Pavol Rusnak 60329f0b65
fix typo 2017-12-17 03:23:37 +01:00
slush 653ed4a67b Added registering custom protobuf messages by application. 2017-12-17 03:17:37 +01:00
slush da335049d7 Removed excessive logging 2017-12-17 02:58:35 +01:00
slush 3fedf44bf5 Bump version to 0.9.0a 2017-12-17 02:31:43 +01:00
slush a27217811b Rework from Google's protobuf to pure-python protobuf implementation 2017-12-17 02:19:16 +01:00
Saleem Rashid 1193b0ee85 transport_udp: Support TREZOR_TRANSPORT_V1 2017-12-16 22:47:19 +01:00
Pavol Rusnak de95c44ad1
trezorctl: cleanup set_homescreen call 2017-12-16 21:29:52 +01:00
Pavol Rusnak b42fc6fb1f
trezorctl: set homescreen for T2 2017-12-13 02:37:59 +01:00
Jochen Hoenicke 6186822f14 Added tool to flash a sector
... and fixed some python3 stuff.
2017-12-12 21:44:30 +01:00
Jonathan Cross 63038e6210 Improve / simplify documentation 2017-12-07 21:57:23 +01:00
Pavol Rusnak 45835733bc
more cleanup 2017-12-02 22:06:44 +01:00
Pavol Rusnak 8b9cba832c
cleanup last commit 2017-12-02 22:02:39 +01:00
slush f5c1587396 Version moved from version.py to __init__.py 2017-12-02 18:48:44 +01:00
slush 59ef832424 Add 'trezorctl version' to track version of installed package 2017-12-02 18:31:57 +01:00
Jonathan Cross 29e4c6a05e Transaction signing example and explanation 2017-12-02 17:36:13 +01:00
Pavol Rusnak 116c3c0575
trezorctl: use click.echo instead of stderr.write 2017-12-02 15:34:06 +01:00
Jonathan Cross ab3d17b3df Better handling of user input for --coin in sign_tx 2017-12-02 15:27:40 +01:00
slush 8c00cda95a Ignore pydev IDE files 2017-12-02 15:21:37 +01:00
Pavol Rusnak ae663ffe0c
client: don't accept non-numerical values for PIN 2017-11-28 19:59:06 +01:00
Jochen Hoenicke 69067c9280 Parse json floats as string
With python-2.7 the float values are sometimes rounded to unacceptable
levels, e.g. stripping the last two digits for values over 100k BTC.
This change parses floats as strings to avoid rounding.

Refactored get_url out of fetch_json to make it easier to add
new tx_api with a different url scheme.
2017-11-28 19:55:31 +01:00
Jonathan Cross ffeb94f792 USAGE: Verbose args & adding native Bech32 segwit 2017-11-24 07:57:44 +01:00
Jochen Hoenicke 2a5888b380 Added missing cached tx for msg_signtx 2017-11-17 21:04:03 +01:00
Jochen Hoenicke 99af1639a6 Updated multisig change test
Use BIP-45 paths with correct change addresses.
This fixes #154.
2017-11-17 21:04:03 +01:00
Pavol Rusnak 225160d7bd
device_tests: op_return now requires confirmation by user 2017-11-15 15:42:28 +01:00
Pavol Rusnak 5730f00ff8
device_tests: disable signtx_zcash 2017-11-15 14:35:34 +01:00
Nicola Larosa 62541cc55f Fix encoding error in trezorlib.client.ProtocolMixin.load_device_by_mnemonic (#153) 2017-11-15 13:33:21 +01:00
Nicola Larosa 5d2d621055 Fix error when using trezorctl to connect to the trezor-core emulator. (#152)
* Fix error when using trezorctl to connect to the trezor-core emulator.

* Restore the ability to specify the host without the port
2017-11-13 22:15:09 +01:00
Pavol Rusnak 0d9ee4376d
use Mnemonic.normalize_string where possible 2017-11-13 22:13:32 +01:00
Jonathan Cross 9e068ce903 Adding xpub example (#149) 2017-11-13 21:54:33 +01:00
Jonathan Cross fa6624129c Adding note: trezorctl is installed on debian / tails (#148) 2017-11-09 17:01:05 +01:00
Jonathan Cross a6562ccc15 README.rst : Adding Mac requirements and simplifying Linux (#147) 2017-11-09 15:53:52 +01:00
Jochen Hoenicke 91a541e862 Expect confirm button for unusual change address (#138)
Changed two tests to different (existing) txs with correct change
address, changed the other tests to expect an additional button
confirmation.
2017-11-08 22:09:28 +01:00
Pavol Rusnak b9b11fa265
style: fix flake8 error 2017-11-08 21:25:15 +01:00
Jonathan Cross 04bb0069c0 USAGE: Adding more examples 2017-11-08 21:22:44 +01:00
Jonathan Cross 972459281b Fixing Bitcoin usage & adding note this is non-SegWit 2017-11-07 15:00:02 +01:00
Pavol Rusnak a5fc76d8c9
don't use generic Exception, but rather specific subclass 2017-11-06 11:10:23 +01:00
Pavol Rusnak 1ab602423c
requirements: add pyblake2 2017-11-06 11:10:23 +01:00
Jochen Hoenicke ebb9540ac2 Added unit tests for sign/verifymessage bech32 2017-11-03 18:32:45 +01:00
Jochen Hoenicke 6d74c6c9df Updated segwit_native tests to bech32 and python3 2017-11-03 18:32:45 +01:00
Jan Pochyla cc9ddcbc12 device_tests: wrap tests in a session 2017-10-31 17:04:27 +01:00
Jan Pochyla fcd793e6e4 transport_hid: force V1 transport with env var 2017-10-31 13:51:13 +01:00
Pavol Rusnak 6a777788ab
trezorctl: use better detection if raw_input replacement is needed 2017-10-25 21:33:33 +02:00
Pavol Rusnak cef2ba0129
flake: silence some new warnings 2017-10-24 01:00:08 +02:00
Pavol Rusnak 23d75bfc10
trezorctl: sign_tx command based on tx_sign_tool by mruddy 2017-10-24 00:50:01 +02:00
Pavol Rusnak 54426761c6
fix transport_hid for python2 2017-10-23 17:28:24 +02:00
Emanuel Haupt fb648a241e Add instructions for FreeBSD users
I've created a FreeBSD port:

537ae1b42c

Add instructions for FreeBSD users.
2017-10-12 10:47:41 +02:00
Pavol Rusnak 0f722c1991
tests: add device test for CoSi 2017-10-04 00:51:32 +02:00
Pavol Rusnak 5057e022c0
trezorlib: move ed25519cosi and ed25519raw from trezor-core 2017-10-04 00:38:53 +02:00
Pavol Rusnak a71c33d123
trezorctl+client: add support for CoSi commit/sign 2017-10-03 18:43:28 +02:00
bithobbes 54df69a407 client.py: matrix recovery info: mention backspace
It is not obvious that it is possible to go back by backspace. Knowing this makes data entry much more comfortable.
2017-09-13 17:54:42 +09:00
Jan Pochyla 66ba2c20c0 transport: add TransportException
Fixes #134
2017-09-05 17:16:04 +02:00
Jan Pochyla ac0184413d transport_hid: refcount for hid handle 2017-09-05 17:15:19 +02:00
Pavol Rusnak dffa93bee3
fix last commit (newline eof) 2017-09-04 17:40:15 +02:00
Jan Pochyla b60ab51f9b transport_hid: more strict interface detection 2017-09-04 17:30:07 +02:00
Pavol Rusnak 2a3f613242
hid: fix product_ids for v2 2017-09-04 14:31:15 +02:00
Jan Pochyla 52f96b3792 transport_hid: raise on missing debuglink 2017-09-04 13:44:19 +02:00
Jan Pochyla 259a61878b tools: update to new transport api 2017-09-04 13:36:31 +02:00
Jan Pochyla 8202971109 rework lazy connecting in client 2017-09-04 13:36:08 +02:00
Jan Pochyla 051f8e961b protocol: 2/3 compat fixes 2017-09-04 11:44:33 +02:00
Jan Pochyla 3d3c2a29d0 client: add missing close method
close() is implemented in some of the mixins to dispose of any resources.
2017-09-04 11:30:34 +02:00
Jan Pochyla c805284a86 tests: fix client init 2017-09-04 11:22:41 +02:00
mruddy 02437d166a fix v1 protocol 2017-09-03 19:34:01 +02:00
Pavol Rusnak c20cea6389
setup: add missing files 2017-09-03 19:15:34 +02:00
Saleem Rashid 29a145154f device_tests: Add test_msg_nem_getaddress 2017-09-03 19:05:29 +02:00
Saleem Rashid eb1d66e27f trezorlib: Add nem_get_address 2017-09-03 19:05:29 +02:00
Saleem Rashid 4f5d9c4323 device_tests: Add test_msg_nem_signtx 2017-09-03 19:05:29 +02:00
Saleem Rashid 58b56bead6 trezorctl: Add nem_sign_tx 2017-09-03 19:05:29 +02:00
Saleem Rashid 991d367416 trezorlib: Update Protocol Buffers 2017-09-03 19:05:29 +02:00
Jan Pochyla 888a1edafa fix style 2017-08-24 14:41:31 +02:00
Jan Pochyla bc42eb68d6 transports: refactor, split protocol code 2017-08-24 14:29:27 +02:00
Jochen Hoenicke 7019438a49 Make -n/--address parameter required.
If you really want to have the master public key, you can still just
give the empty string.
* Changed -address to --address everywhere.
* Added help for this parameter.
* Added required flag.
2017-08-20 16:33:49 +02:00
Pavol Rusnak a4a7aa8d85
trezorctl: fix hexlify calls on python3 2017-08-17 21:18:49 +02:00
Jan Pochyla e3c7146a80 tests: add multisig marker 2017-08-15 17:33:11 +02:00
Jochen Hoenicke 5cfa973a78 Fixed white-spaces 2017-08-13 20:49:23 +02:00
Jochen Hoenicke 600bcba69c Updated unit tests for BitcoinCash 2017-08-13 20:49:23 +02:00
Jochen Hoenicke 29f29626fc Added multisig test 2017-08-13 20:49:23 +02:00
Pavol Rusnak ff157264a2
trezorctl: print message name to output 2017-08-09 00:52:52 +02:00
Pavol Rusnak 8a663f7ec3
tests: rename BCC to BCH, Bitcoin Cash to Bcash 2017-07-31 15:29:11 +02:00
Pavol Rusnak 32fa08f38b
tests: remove estimate_tx_size 2017-07-31 14:00:26 +02:00
Pavol Rusnak b6dc73ce9c
tests: fix bytes/str problem in test_msg_ethereum_signtx 2017-07-31 13:56:25 +02:00
Saleem Rashid b469519e26 client: fix matrix recovery, use named enums, use isdigit(), ignore broken E721 test 2017-07-31 13:35:31 +02:00
Pavol Rusnak 81d5170c10
tests: start rewriting device tests to pytest 2017-07-28 18:07:20 +02:00
Pavol Rusnak 8a1d211ee9
tests: fix flake8 errors 2017-07-28 16:08:33 +02:00
Jan Pochyla 6df01fbfa3 TransportV2: adapt to recent changes
- remove checksum
- add sequence numbers
2017-07-28 15:58:20 +02:00
Pavol Rusnak 5309baf48e
tests: reduce unhexlify reuse 2017-07-28 15:24:18 +02:00
Pavol Rusnak 11bfacc9b3
tests: bytes are not necessary in unhexlify 2017-07-28 15:17:19 +02:00
Jochen Hoenicke 9917d9ebfc
New unit tests for signing (bitcoin cash) 2017-07-27 21:51:00 +02:00
Pavol Rusnak efe36b3a2f
unit_tests: rewrite from unittest to pytest 2017-07-26 14:48:20 +02:00
Pavol Rusnak 11b686a9f2
tests: add tests for Segwit SignMessage/VerifyMessage 2017-07-25 19:29:28 +02:00
Pavol Rusnak 8133317172
add SignMessage.script_type 2017-07-24 16:11:38 +02:00
Pavol Rusnak de6402e95e
implement set_flags (aka ApplyFlags) 2017-07-17 18:37:15 +02:00
Pavol Rusnak 36985519b5
tests: add test for ethereum sign/verify message 2017-07-17 14:40:35 +02:00
Pavol Rusnak afdd27c551
trezorctl: fix set_homescreen for python3 2017-07-16 15:03:01 +02:00
Pavol Rusnak d865c0ea31
trezorctl: load_device -s now loads SLIP-0014 mnemonic, --skip-checksum renamed to --ignore-checksum 2017-07-16 14:54:50 +02:00
Pavol Rusnak 23ab43d612
ethereum: implement EthereumSignMessage/EthereumVerifyMessage 2017-07-12 18:35:54 +02:00
Pavol Rusnak 20aebed394
trezorctl: seems that click.Choice is not friends with ints 2017-07-12 15:45:16 +02:00
Pavol Rusnak ee5f53d4be
fix ethereum_get_address for python3 2017-07-11 19:37:25 +02:00
Pavol Rusnak c7a2c72a75
simplify SelfTest.payload 2017-07-10 19:08:16 +02:00
Pavol Rusnak 5ffc700f0c
use isinstance to detect whether result is protobuf message 2017-07-10 18:20:32 +02:00
Pavol Rusnak 1727b9a9b6
add SelfTest.payload 2017-07-10 17:40:13 +02:00
Pavol Rusnak f73fc33439
drop internal use of protobuf_json, use json_format provided by google.protobuf 2017-07-10 15:36:44 +02:00
Pavol Rusnak 2d643031ac
change dependency to protobuf>=3.0.0 2017-07-10 14:41:56 +02:00
Pavol Rusnak e6acf90f2b
tests: add test for broken BackupDevice workflow 2017-07-05 13:03:06 +02:00
Pavol Rusnak 5a89a15935
trezorctl: use more idiomatic approach using resultcallback 2017-07-05 12:55:39 +02:00
Pavol Rusnak b335d30b8d
use click in trezorctl 2017-07-05 12:55:38 +02:00
Pavol Rusnak 0ee1667c6f
trezorctl: cleanup 2017-07-05 12:55:38 +02:00
Pavol Rusnak d33e9a178b
bump version to 0.8.0a0 2017-07-05 12:55:37 +02:00
296 changed files with 10433 additions and 10882 deletions

19
.flake8
View File

@ -1,17 +1,32 @@
[flake8]
filename =
*.py,
trezorctl
exclude =
.tox/,
build/,
dist/,
trezorlib/*_pb2.py
ignore =
# F821 undefined name 'unicode',
# F821 undefined name 'unicode'
F821,
# F841 local variable is assigned to but never used
F841,
# F401: module imported but unused
F401,
# F403: used import *
F403,
# F405 'foo' may be undefined, or defined from star imports
F405,
# E241: multiple spaces after ':'
E241,
# E402: module level import not at top of file
E402,
# E501: line too long
E501
E501,
# E721: do not compare types, use 'isinstance()'
E721,
# E722: do not use bare except
E722,
# E741: ambiguous variable name
E741

5
.gitignore vendored
View File

@ -1,13 +1,16 @@
.project
.pydevproject
MANIFEST
build/
dist/
python_trezor.egg-info/
trezor.egg-info/
*.pyc
*.bin
*.png
*.py.cache
distribute-*.egg
distribute-*.tar.gz
docs/_build
docs/.docs-build-environment
.tox/
.cache/

View File

@ -15,7 +15,7 @@ addons:
- libusb-1.0-0-dev
python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "3.6"
@ -30,6 +30,7 @@ install:
script:
- python setup.py install
- flake8
- flake8 trezorctl
- tox
notifications:

10
CHANGELOG.md Normal file
View File

@ -0,0 +1,10 @@
The changelog format is bound to change as we figure out a way to autogenerate it.
## version 0.9.1 (released 2018-03-05)
- proper support for Trezor model T
- gradually dropping Python 2 compatibility (pypi package will now be marked as Python 3 only)
- support for Monacoin
- improvements to `trezorctl`:
- add pretty-printing of features and protobuf debug dumps (fixes #199)
- support `TREZOR_PATH` environment variable to preselect a Trezor device.

View File

@ -1,3 +1,3 @@
recursive-include tests *.py *.sh *.json
recursive-include bash_completion.d *.sh
include trezorlib/tests/txcache/*.json
include COPYING

View File

@ -7,85 +7,55 @@ python-trezor
.. image:: https://badges.gitter.im/trezor/community.svg
:target: https://gitter.im/trezor/community
Python library for communicating with TREZOR Hardware Wallet
Python library and commandline client for communicating with TREZOR Hardware Wallet
See https://trezor.io for more information
Install
-------
(Run with sudo if not running in superuser mode under Linux)
Linux requirements:
.. code::
pip install trezor
sudo apt-get install python3-dev cython3 libusb-1.0-0-dev libudev-dev git
On Linux you might need to run these commands first:
Linux & Mac Python requirements:
.. code::
sudo apt-get install python-dev cython libusb-1.0-0-dev libudev-dev git
sudo pip install setuptools
sudo -H pip3 install setuptools
sudo -H pip3 install -r requirements.txt
sudo -H pip3 install trezor
Usage
-----
There is a command line tool called ``trezorctl`` which can perform various tasks. Use the following to learn about its commands:
On FreeBSD you can install the packages:
.. code::
trezorctl --help
pkg install security/py-trezor
or to learn options of a particular command:
or build via ports:
.. code::
trezorctl commands --help
cd /usr/ports/security/py-trezor
make install clean
or visit `usage <USAGE.rst>`_ page for more info.
To use the library in your application look at the following example.
Commandline client (trezorctl)
---------------------------
Example
-------
The included ``trezorctl`` python script can perform various tasks such as changing setting in the Trezor, signing transactions, retrieving account info and addresses. See the `docs/ <docs/>`_ sub folder for detailed examples and options.
also found in ``tools/helloworld.py``
NOTE: An older version of the ``trezorctl`` command is `available for Debian Stretch <https://packages.debian.org/en/stretch/python-trezor>`_ (and comes pre-installed on `Tails OS <https://tails.boum.org/>`_).
.. code:: python
#!/usr/bin/env python
Python Library
--------------
from trezorlib.client import TrezorClient
from trezorlib.transport_hid import HidTransport
You can use this python library to interact with a Bitcoin Trezor and use its capabilities in your application.
See examples here in the `tools/ <tools/>`_ sub folder.
def main():
# List all connected TREZORs on USB
devices = HidTransport.enumerate()
# Check whether we found any
if len(devices) == 0:
print('No TREZOR found')
return
# Use first connected device
transport = HidTransport(devices[0])
# Creates object for manipulating TREZOR
client = TrezorClient(transport)
# Print out TREZOR's features and settings
print(client.features)
# Get the first address of first BIP44 account
# (should be the same address as shown in wallet.trezor.io)
bip32_path = client.expand_path("44'/0'/0'/0/0")
address = client.get_address('Bitcoin', bip32_path)
print('Bitcoin address:', address)
client.close()
if __name__ == '__main__':
main()
PIN Entering
------------

View File

@ -1,14 +0,0 @@
Usage
=====
Get first receiving address of first account for Bitcoin:
.. code::
trezorctl get_address -c Litecoin -t address -n "m/44'/0'/0'/0/0"
Get first receiving address of first account for Segwit-in-P2SH for Litecoin:
.. code::
trezorctl get_address -c Litecoin -t p2shsegwit -n "m/49'/2'/0'/0/0"

View File

@ -1,16 +0,0 @@
#!/bin/bash
CURDIR=$(pwd)
cd $CURDIR/../trezor-common/protob
for i in messages types ; do
protoc --python_out=$CURDIR/trezorlib/ -I/usr/include -I. $i.proto
done
# hack to make output python 3 compatible
sed -i 's/^import types_pb2/from . import types_pb2/g' $CURDIR/trezorlib/messages_pb2.py
# add version
PROTOC_VER=$(protoc --version)
PROTOB_REV=$(git rev-parse HEAD)
sed -i "3i# $PROTOC_VER\n# trezor-common $PROTOB_REV" $CURDIR/trezorlib/*_pb2.py

120
docs/EXAMPLES.rst Normal file
View File

@ -0,0 +1,120 @@
Examples demonstrating how to use trezorctl
===========================================
Show all available `options <OPTIONS.rst>`_:
.. code::
trezorctl --help
Retrieve features, settings and coin types supported by your device:
.. code::
trezorctl get_features
Bitcoin examples
----------------
Get first receiving address of first account for Bitcoin (Legacy / non-SegWit):
.. code::
trezorctl get_address --coin Bitcoin --script-type address --address "m/44'/0'/0'/0/0"
Get first receiving address of first account for Bitcoin (Bech32 native SegWit P2WPKH):
.. code::
trezorctl get_address --coin Bitcoin --script-type segwit --address "m/49'/0'/0'/0/0"
Get first receiving address of first account for Bitcoin (SegWit-in-P2SH):
.. code::
trezorctl get_address --coin Bitcoin --script-type p2shsegwit --address "m/49'/0'/0'/0/0"
Get Legacy Bitcoin ``xpub`` (can be used to create a watch-only wallet):
.. code::
trezorctl get_public_node --coin Bitcoin --address "m/44'/0'/0'"
Transaction signing
-------------------
You can use ``trezorctl`` to sign a transaction without it automatically being broadcast to the Bitcoin network.
You will need the following pieces of info:
1) Transaction ID containing the Output we want to spend (aka ``prevhash`` or ``a5ea715a...d201e64e`` in example below).
2) Index number of the Output being spent from the above tx (aka ``previndex`` or ``0`` in example below).
3) BIP32 path to the Node which can spend the above UTXO (eg ``Bitcoin/0'/0/0`` for the first).
4) Destination address where you want to send funds (eg ``3M8XGFBKwkf7miBzpkU3x2DoWwAVrD1mhk`` below).
5) Amount to send in satoshis - ``91305`` in the example below (multiply BTC amount 0.00091305 by 100,000,000).
6) Expected fee (``0.00019695`` BTC in example below). Note: the miner receives all satoshis left unspent from a transaction. If you want to receive some change, you need to send it to an address you own (otherwise it will go to miner). Fee is not needed below, we just want it as a sanity check.
There are many ways to retrieve the info above: from a watch-only wallet in Bitcoin Core, https://coinb.in (`screenshot <sign_tx-coinb.in.png>`_) etc. The easiest way is using the Trezor online wallet: https://beta-wallet.trezor.io
After authenticating, open the "Send" tab, fill-out all details, then open the "Show transaction details" menu to see the info needed above (`screenshot <sign_tx-trezor.io.png>`_). Once you have the required details, you can then perform the transaction signing using ``trezorctl`` as shown in the example below:
.. code::
trezorctl sign_tx -c Bitcoin
Input (prevhash:previndex, empty to move on): a5ea715aa99ca30516f3af6f622dfe7399d883d49ad74b1fe33fdf73d201e64e:0
Node path to sign with (e.g.- Bitcoin/0'/0/0): Bitcoin/0'/0/0
Input (prevhash:previndex, empty to move on):
Pay to address (empty to move on): 3M8XGFBKwkf7miBzpkU3x2DoWwAVrD1mhk
Amount (in satoshis): 91305
Pay to address (empty to move on):
Passphrase required:
Confirm your Passphrase:
RECEIVED PART OF SERIALIZED TX (152 BYTES)
RECEIVED PART OF SERIALIZED TX (37 BYTES)
SIGNED IN 52.538 SECONDS, CALLED 10 MESSAGES, 189 BYTES
Signed Transaction:
01000000014ee601d273df3fe31f4bd79ad483d89973fe2d626faff31605a39ca95a71eaa5000000006a47304402206386a0ad0f0b196d375a0805eee2aebe4644032c2998aaf00e43ce68a293986702202ad25964844657e10130f81201b7d87eb8047cf0c09dfdcbbe68a1a732e80ded012103b375a0dd50c8dbc4a6156a55e31274ee0537191e1bc824a09278a220fafba2dbffffffff01a96401000000000017a914d53d47ccd1579b93c284e9caf3c81f3f417871698700000000
Use the following form to broadcast it to the network:
https://btc-bitcore1.trezor.io/tx/send
The signed transaction text can then be inspected in Electrum (`screenshot <sign_tx-electrum2.png>`_), `coinb.in <https://coinb.in/?verify=01000000014ee601d273df3fe31f4bd79ad483d89973fe2d626faff31605a39ca95a71eaa5000000006a47304402206386a0ad0f0b196d375a0805eee2aebe4644032c2998aaf00e43ce68a293986702202ad25964844657e10130f81201b7d87eb8047cf0c09dfdcbbe68a1a732e80ded012103b375a0dd50c8dbc4a6156a55e31274ee0537191e1bc824a09278a220fafba2dbffffffff01a96401000000000017a914d53d47ccd1579b93c284e9caf3c81f3f417871698700000000#verify>`_ or another tool. If all info is correct, you can then broadcast the tx to the Bitcoin network via the URL provided by ``trezorctl`` or Electrum (Tools → Load transaction → From text. Here is a `screenshot <sign_tx-electrum1.png>`_). TIP: Electrum will only show the transaction fee if you previously imported the spending address (eg ``16ijWp48xn8hj6deD5ZHSJcgNjtYbpiki8`` from example tx above). Also, the final tx size (and therefore satoshis / byte) might be slightly different than the estimate shown on beta-wallet.trezor.io
The final broadcast and mined transaction can be seen here: https://blockchain.info/tx/270684c14be85efec9adafa50339fd120658381ed2300b9207d0a0df2a5f0bf9
Litecoin examples
-----------------
Get first receiving address of first account for Litecoin (Bech32 native SegWit P2WPKH):
.. code::
trezorctl get_address --coin Litecoin --script-type segwit --address "m/49'/2'/0'/0/0"
Get first receiving address of first account for Litecoin (SegWit-in-P2SH):
.. code::
trezorctl get_address --coin Litecoin --script-type p2shsegwit --address "m/49'/2'/0'/0/0"
Notes
-----
1. Bech32 native SegWit encoded addresses require `Trezor Firmware v1.6.0 <https://github.com/trezor/trezor-mcu/releases>`_ or later.

63
docs/OPTIONS.rst Normal file
View File

@ -0,0 +1,63 @@
Commandline options for trezorctl
=================================
See `EXAMPLES.rst <EXAMPLES.rst>`_ for examples on how to use.
Use the following command to see all options:
.. code::
trezorctl --help
.. code::
Usage: trezorctl [OPTIONS] COMMAND [ARGS]...
Options:
-t, --transport [usb|udp|pipe|bridge]
Select transport used for communication.
-p, --path TEXT Select device by transport-specific path.
-v, --verbose Show communication messages.
-j, --json Print result as JSON object
--help Show this message and exit.
Commands:
backup_device Perform device seed backup.
change_pin Change new PIN or remove existing.
clear_session Clear session (remove cached PIN, passphrase,...
cosi_commit Ask device to commit to CoSi signing.
cosi_sign Ask device to sign using CoSi.
decrypt_keyvalue Decrypt value by given key and path.
decrypt_message Decrypt message.
disable_passphrase Disable passphrase.
enable_passphrase Enable passphrase.
encrypt_keyvalue Encrypt value by given key and path.
encrypt_message Encrypt message.
ethereum_get_address Get Ethereum address in hex encoding.
ethereum_sign_message Sign message with Ethereum address.
ethereum_sign_tx Sign (and optionally publish) Ethereum...
ethereum_verify_message Verify message signed with Ethereum address.
firmware_update Upload new firmware to device (must be in...
get_address Get address for specified path.
get_entropy Get example entropy.
get_features Retrieve device features and settings.
get_public_node Get public node of given path.
list List connected TREZOR devices.
list_coins List all supported coin types by the device.
load_device Load custom configuration to the device.
nem_get_address Get NEM address for specified path.
nem_sign_tx Sign (and optionally broadcast) NEM...
ping Send ping message.
recovery_device Start safe recovery workflow.
reset_device Perform device setup and generate new seed.
self_test Perform a self-test.
set_flags Set device flags.
set_homescreen Set new homescreen.
set_label Set new device label.
set_u2f_counter Set U2F counter.
sign_message Sign message using address of given path.
sign_tx Sign transaction.
verify_message Verify message.
version Show version of trezorctl/trezorlib.
wipe_device Reset device to factory defaults and remove...

5
docs/README.rst Normal file
View File

@ -0,0 +1,5 @@
Documentation for trezorctl commandline client
==============================================
* `EXAMPLES.rst <EXAMPLES.rst>`_ - Examples demonstrating how to use trezorctl
* `OPTIONS.rst <OPTIONS.rst>`_ - Commandline options for trezorctl

BIN
docs/sign_tx-coinb.in.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/sign_tx-electrum1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/sign_tx-electrum2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/sign_tx-trezor.io.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,5 +1,8 @@
ecdsa>=0.9
protobuf>=3.1.0
mnemonic>=0.17
hidapi>=0.7.99.post20
requests>=2.4.0
click>=6.2
pyblake2>=0.9.3
hidapi>=0.7.99.post20
libusb1>=1.6.4
rlp>=0.6.0

View File

@ -1,12 +1,14 @@
#!/usr/bin/env python
#!/usr/bin/env python3
from setuptools import setup
install_requires = [
'ecdsa>=0.9',
'protobuf>=3.1.0',
'mnemonic>=0.17',
'setuptools>=19.0',
'ecdsa>=0.9',
'mnemonic>=0.17',
'requests>=2.4.0',
'click>=6.2',
'pyblake2>=0.9.3',
'rlp>=0.6.0',
]
import sys
@ -15,32 +17,31 @@ if '--disable-hidapi' in sys.argv:
else:
install_requires.append('hidapi>=0.7.99.post20')
if '--disable-libusb' in sys.argv:
sys.argv.remove('--disable-libusb')
else:
install_requires.append('libusb1>=1.6.4')
from trezorlib import __version__ as VERSION
setup(
name='trezor',
version='0.7.16',
version=VERSION,
author='TREZOR',
author_email='info@trezor.io',
description='Python library for communicating with TREZOR Hardware Wallet',
url='https://github.com/trezor/python-trezor',
py_modules=[
'trezorlib.ckd_public',
'trezorlib.client',
'trezorlib.debuglink',
'trezorlib.mapping',
'trezorlib.messages_pb2',
'trezorlib.protobuf_json',
'trezorlib.qt.pinmatrix',
'trezorlib.tools',
packages=[
'trezorlib',
'trezorlib.transport',
'trezorlib.transport_bridge',
'trezorlib.transport_hid',
'trezorlib.transport_pipe',
'trezorlib.transport_udp',
'trezorlib.tx_api',
'trezorlib.types_pb2',
'trezorlib.messages',
'trezorlib.qt',
'trezorlib.tests.device_tests',
'trezorlib.tests.unit_tests',
],
scripts=['trezorctl'],
install_requires=install_requires,
python_requires='>=3.3',
include_package_data=True,
zip_safe=False,
classifiers=[
@ -48,5 +49,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Operating System :: Microsoft :: Windows',
'Operating System :: MacOS :: MacOS X',
'Programming Language :: Python :: 3 :: Only',
],
)

View File

@ -1,85 +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>
#
# 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 print_function
import sys
sys.path = ['../../'] + sys.path
try:
from trezorlib.transport_hid import HidTransport
HID_ENABLED = True
except Exception as e:
print('HID transport disabled:', e.message, e.args)
HID_ENABLED = False
try:
from trezorlib.transport_pipe import PipeTransport
PIPE_ENABLED = True
except Exception as e:
print('PIPE transport disabled:', e.message, e.args)
PIPE_ENABLED = False
try:
from trezorlib.transport_udp import UdpTransport
UDP_ENABLED = True
except Exception as e:
print('UDP transport disabled:', e.message, e.args)
UDP_ENABLED = False
def pipe_exists(path):
import os
import stat
try:
return stat.S_ISFIFO(os.stat(path).st_mode)
except:
return False
if HID_ENABLED and len(HidTransport.enumerate()) > 0:
devices = HidTransport.enumerate()
print('Using TREZOR')
TRANSPORT = HidTransport
TRANSPORT_ARGS = (devices[0],)
TRANSPORT_KWARGS = {'debug_link': False}
DEBUG_TRANSPORT = HidTransport
DEBUG_TRANSPORT_ARGS = (devices[0],)
DEBUG_TRANSPORT_KWARGS = {'debug_link': True}
elif PIPE_ENABLED and pipe_exists('/tmp/pipe.trezor.to'):
print('Using Emulator (v1=pipe)')
TRANSPORT = PipeTransport
TRANSPORT_ARGS = ('/tmp/pipe.trezor', False)
TRANSPORT_KWARGS = {}
DEBUG_TRANSPORT = PipeTransport
DEBUG_TRANSPORT_ARGS = ('/tmp/pipe.trezor_debug', False)
DEBUG_TRANSPORT_KWARGS = {}
elif UDP_ENABLED:
print('Using Emulator (v2=udp)')
TRANSPORT = UdpTransport
TRANSPORT_ARGS = ('', )
TRANSPORT_KWARGS = {}
DEBUG_TRANSPORT = UdpTransport
DEBUG_TRANSPORT_ARGS = ('', )
DEBUG_TRANSPORT_KWARGS = {}

View File

@ -1,5 +0,0 @@
#!/bin/bash
for i in test_*.py; do
echo Starting: $i
python $i > $i.out 2> $i.err
done

View File

@ -1,3 +0,0 @@
#!/bin/bash
python -m unittest discover

View File

@ -1,92 +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>
#
# 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/>.
import unittest
import common
import binascii
class TestMsgCipherkeyvalue(common.TrezorTest):
def test_encrypt(self):
self.setup_mnemonic_nopin_nopassphrase()
# different ask values
res = self.client.encrypt_keyvalue([0, 1, 2], b"test", b"testing message!", ask_on_encrypt=True, ask_on_decrypt=True)
self.assertEqual(binascii.hexlify(res), b'676faf8f13272af601776bc31bc14e8f')
res = self.client.encrypt_keyvalue([0, 1, 2], b"test", b"testing message!", ask_on_encrypt=True, ask_on_decrypt=False)
self.assertEqual(binascii.hexlify(res), b'5aa0fbcb9d7fa669880745479d80c622')
res = self.client.encrypt_keyvalue([0, 1, 2], b"test", b"testing message!", ask_on_encrypt=False, ask_on_decrypt=True)
self.assertEqual(binascii.hexlify(res), b'958d4f63269b61044aaedc900c8d6208')
res = self.client.encrypt_keyvalue([0, 1, 2], b"test", b"testing message!", ask_on_encrypt=False, ask_on_decrypt=False)
self.assertEqual(binascii.hexlify(res), b'e0cf0eb0425947000eb546cc3994bc6c')
# different key
res = self.client.encrypt_keyvalue([0, 1, 2], b"test2", b"testing message!", ask_on_encrypt=True, ask_on_decrypt=True)
self.assertEqual(binascii.hexlify(res), b'de247a6aa6be77a134bb3f3f925f13af')
# different message
res = self.client.encrypt_keyvalue([0, 1, 2], b"test", b"testing message! it is different", ask_on_encrypt=True, ask_on_decrypt=True)
self.assertEqual(binascii.hexlify(res), b'676faf8f13272af601776bc31bc14e8f3ae1c88536bf18f1b44f1e4c2c4a613d')
# different path
res = self.client.encrypt_keyvalue([0, 1, 3], b"test", b"testing message!", ask_on_encrypt=True, ask_on_decrypt=True)
self.assertEqual(binascii.hexlify(res), b'b4811a9d492f5355a5186ddbfccaae7b')
def test_decrypt(self):
self.setup_mnemonic_nopin_nopassphrase()
# different ask values
res = self.client.decrypt_keyvalue([0, 1, 2], b"test", binascii.unhexlify("676faf8f13272af601776bc31bc14e8f"), ask_on_encrypt=True, ask_on_decrypt=True)
self.assertEqual(res, b'testing message!')
res = self.client.decrypt_keyvalue([0, 1, 2], b"test", binascii.unhexlify("5aa0fbcb9d7fa669880745479d80c622"), ask_on_encrypt=True, ask_on_decrypt=False)
self.assertEqual(res, b'testing message!')
res = self.client.decrypt_keyvalue([0, 1, 2], b"test", binascii.unhexlify("958d4f63269b61044aaedc900c8d6208"), ask_on_encrypt=False, ask_on_decrypt=True)
self.assertEqual(res, b'testing message!')
res = self.client.decrypt_keyvalue([0, 1, 2], b"test", binascii.unhexlify("e0cf0eb0425947000eb546cc3994bc6c"), ask_on_encrypt=False, ask_on_decrypt=False)
self.assertEqual(res, b'testing message!')
# different key
res = self.client.decrypt_keyvalue([0, 1, 2], b"test2", binascii.unhexlify("de247a6aa6be77a134bb3f3f925f13af"), ask_on_encrypt=True, ask_on_decrypt=True)
self.assertEqual(res, b'testing message!')
# different message
res = self.client.decrypt_keyvalue([0, 1, 2], b"test", binascii.unhexlify("676faf8f13272af601776bc31bc14e8f3ae1c88536bf18f1b44f1e4c2c4a613d"), ask_on_encrypt=True, ask_on_decrypt=True)
self.assertEqual(res, b'testing message! it is different')
# different path
res = self.client.decrypt_keyvalue([0, 1, 3], b"test", binascii.unhexlify("b4811a9d492f5355a5186ddbfccaae7b"), ask_on_encrypt=True, ask_on_decrypt=True)
self.assertEqual(res, b'testing message!')
def test_encrypt_badlen(self):
self.setup_mnemonic_nopin_nopassphrase()
self.assertRaises(Exception, self.client.encrypt_keyvalue, [0, 1, 2], b"test", b"testing")
def test_decrypt_badlen(self):
self.setup_mnemonic_nopin_nopassphrase()
self.assertRaises(Exception, self.client.decrypt_keyvalue, [0, 1, 2], b"test", b"testing")
if __name__ == '__main__':
unittest.main()

View File

@ -1,54 +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>
#
# 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/>.
import unittest
import binascii
import common
import trezorlib.types_pb2 as proto_types
class TestMsgEstimatetxsize(common.TrezorTest):
def test_estimate_size(self):
self.setup_mnemonic_nopin_nopassphrase()
inp1 = proto_types.TxInputType(
address_n=[0], # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e
# amount=390000,
prev_hash=binascii.unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'),
prev_index=0,
)
out1 = proto_types.TxOutputType(
address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
amount=390000 - 10000,
script_type=proto_types.PAYTOADDRESS,
)
est_size = self.client.estimate_tx_size('Bitcoin', [inp1, ], [out1, ])
self.assertEqual(est_size, 194)
(_, tx) = self.client.sign_tx('Bitcoin', [inp1, ], [out1, ])
real_size = len(tx)
self.assertGreaterEqual(est_size, real_size)
if __name__ == '__main__':
unittest.main()

View File

@ -1,250 +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>
#
# 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/>.
import unittest
import common
import binascii
class TestMsgEthereumSigntx(common.TrezorTest):
def test_ethereum_signtx_nodata(self):
self.setup_mnemonic_nopin_nopassphrase()
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0, 0],
nonce=0,
gas_price=20,
gas_limit=20,
to=binascii.unhexlify('1d1c328764a41bda0492b66baa30c4a339ff85ef'),
value=10)
self.assertEqual(sig_v, 27)
self.assertEqual(binascii.hexlify(sig_r), '9b61192a161d056c66cfbbd331edb2d783a0193bd4f65f49ee965f791d898f72')
self.assertEqual(binascii.hexlify(sig_s), '49c0bbe35131592c6ed5c871ac457feeb16a1493f64237387fab9b83c1a202f7')
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0, 0],
nonce=123456,
gas_price=20000,
gas_limit=20000,
to=binascii.unhexlify('1d1c328764a41bda0492b66baa30c4a339ff85ef'),
value=12345678901234567890)
self.assertEqual(sig_v, 28)
self.assertEqual(binascii.hexlify(sig_r), '6de597b8ec1b46501e5b159676e132c1aa78a95bd5892ef23560a9867528975a')
self.assertEqual(binascii.hexlify(sig_s), '6e33c4230b1ecf96a8dbb514b4aec0a6d6ba53f8991c8143f77812aa6daa993f')
def test_ethereum_signtx_data(self):
self.setup_mnemonic_nopin_nopassphrase()
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0, 0],
nonce=0,
gas_price=20,
gas_limit=20,
to=binascii.unhexlify('1d1c328764a41bda0492b66baa30c4a339ff85ef'),
value=10,
data='abcdefghijklmnop' * 16)
self.assertEqual(sig_v, 28)
self.assertEqual(binascii.hexlify(sig_r), '6da89ed8627a491bedc9e0382f37707ac4e5102e25e7a1234cb697cedb7cd2c0')
self.assertEqual(binascii.hexlify(sig_s), '691f73b145647623e2d115b208a7c3455a6a8a83e3b4db5b9c6d9bc75825038a')
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0, 0],
nonce=123456,
gas_price=20000,
gas_limit=20000,
to=binascii.unhexlify('1d1c328764a41bda0492b66baa30c4a339ff85ef'),
value=12345678901234567890,
data='ABCDEFGHIJKLMNOP' * 256 + '!!!')
self.assertEqual(sig_v, 28)
self.assertEqual(binascii.hexlify(sig_r), '4e90b13c45c6a9bf4aaad0e5427c3e62d76692b36eb727c78d332441b7400404')
self.assertEqual(binascii.hexlify(sig_s), '3ff236e7d05f0f9b1ee3d70599bb4200638f28388a8faf6bb36db9e04dc544be')
def test_ethereum_signtx_message(self):
self.setup_mnemonic_nopin_nopassphrase()
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0, 0],
nonce=0,
gas_price=20000,
gas_limit=20000,
to=binascii.unhexlify('1d1c328764a41bda0492b66baa30c4a339ff85ef'),
value=0,
data='ABCDEFGHIJKLMNOP' * 256 + '!!!')
self.assertEqual(sig_v, 28)
self.assertEqual(binascii.hexlify(sig_r), '070e9dafda4d9e733fa7b6747a75f8a4916459560efb85e3e73cd39f31aa160d')
self.assertEqual(binascii.hexlify(sig_s), '7842db33ef15c27049ed52741db41fe3238a6fa3a6a0888fcfb74d6917600e41')
def test_ethereum_signtx_newcontract(self):
self.setup_mnemonic_nopin_nopassphrase()
# contract creation without data should fail.
self.assertRaises(
Exception,
self.client.ethereum_sign_tx,
n=[0, 0],
nonce=123456,
gas_price=20000,
gas_limit=20000,
to='',
value=12345678901234567890
)
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0, 0],
nonce=0,
gas_price=20000,
gas_limit=20000,
to='',
value=12345678901234567890,
data='ABCDEFGHIJKLMNOP' * 256 + '!!!')
self.assertEqual(sig_v, 28)
self.assertEqual(binascii.hexlify(sig_r), 'b401884c10ae435a2e792303b5fc257a09f94403b2883ad8c0ac7a7282f5f1f9')
self.assertEqual(binascii.hexlify(sig_s), '4742fc9e6a5fa8db3db15c2d856914a7f3daab21603a6c1ce9e9927482f8352e')
def test_ethereum_sanity_checks(self):
# gas overflow
self.assertRaises(
Exception,
self.client.ethereum_sign_tx,
n=[0, 0],
nonce=123456,
gas_price=0xffffffffffffffffffffffffffffffff,
gas_limit=0xffffffffffffffffffffffffffffff,
to=binascii.unhexlify('1d1c328764a41bda0492b66baa30c4a339ff85ef'),
value=12345678901234567890
)
# no gas price
self.assertRaises(
Exception,
self.client.ethereum_sign_tx,
n=[0, 0],
nonce=123456,
gas_limit=10000,
to=binascii.unhexlify('1d1c328764a41bda0492b66baa30c4a339ff85ef'),
value=12345678901234567890
)
# no gas limit
self.assertRaises(
Exception,
self.client.ethereum_sign_tx,
n=[0, 0],
nonce=123456,
gas_price=10000,
to=binascii.unhexlify('1d1c328764a41bda0492b66baa30c4a339ff85ef'),
value=12345678901234567890
)
# no nonce
self.assertRaises(
Exception,
self.client.ethereum_sign_tx,
n=[0, 0],
gas_price=10000,
gas_limit=123456,
to=binascii.unhexlify('1d1c328764a41bda0492b66baa30c4a339ff85ef'),
value=12345678901234567890
)
def test_ethereum_signtx_nodata_eip155(self):
self.setup_mnemonic_allallall()
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0x80000000 | 44, 0x80000000 | 1, 0x80000000, 0, 0],
nonce=0,
gas_price=20000000000,
gas_limit=21000,
to=binascii.unhexlify('8ea7a3fccc211ed48b763b4164884ddbcf3b0a98'),
value=100000000000000000,
chain_id=3)
self.assertEqual(sig_v, 41)
self.assertEqual(binascii.hexlify(sig_r), 'a90d0bc4f8d63be69453dd62f2bb5fff53c610000abf956672564d8a654d401a')
self.assertEqual(binascii.hexlify(sig_s), '544a2e57bc8b4da18660a1e6036967ea581cc635f5137e3ba97a750867c27cf2')
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0x80000000 | 44, 0x80000000 | 1, 0x80000000, 0, 0],
nonce=1,
gas_price=20000000000,
gas_limit=21000,
to=binascii.unhexlify('8ea7a3fccc211ed48b763b4164884ddbcf3b0a98'),
value=100000000000000000,
chain_id=3)
self.assertEqual(sig_v, 42)
self.assertEqual(binascii.hexlify(sig_r), '699428a6950e23c6843f1bf3754f847e64e047e829978df80d55187d19a401ce')
self.assertEqual(binascii.hexlify(sig_s), '087343d0a3a2f10842218ffccb146b59a8431b6245ab389fde22dc833f171e6e')
def test_ethereum_signtx_data_eip155(self):
self.setup_mnemonic_allallall()
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0x80000000 | 44, 0x80000000 | 1, 0x80000000, 0, 0],
nonce=2,
gas_price=20000000000,
gas_limit=21004,
to=binascii.unhexlify('8ea7a3fccc211ed48b763b4164884ddbcf3b0a98'),
value=100000000000000000,
data='\0',
chain_id=3)
self.assertEqual(sig_v, 42)
self.assertEqual(binascii.hexlify(sig_r), 'ba85b622a8bb82606ba96c132e81fa8058172192d15bc41d7e57c031bca17df4')
self.assertEqual(binascii.hexlify(sig_s), '6473b75997634b6f692f8d672193591d299d5bf1c2d6e51f1a14ed0530b91c7d')
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0x80000000 | 44, 0x80000000 | 1, 0x80000000, 0, 0],
nonce=3,
gas_price=20000000000,
gas_limit=299732,
to=binascii.unhexlify('8ea7a3fccc211ed48b763b4164884ddbcf3b0a98'),
value=100000000000000000,
data='ABCDEFGHIJKLMNOP' * 256 + '!!!',
chain_id=3)
self.assertEqual(sig_v, 42)
self.assertEqual(binascii.hexlify(sig_r), 'd021c98f92859c8db5e4de2f0e410a8deb0c977eb1a631e323ebf7484bd0d79a')
self.assertEqual(binascii.hexlify(sig_s), '2c0e9defc9b1e895dc9520ff25ba3c635b14ad70aa86a5ad6c0a3acb82b569b6')
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0x80000000 | 44, 0x80000000 | 1, 0x80000000, 0, 0],
nonce=4,
gas_price=20000000000,
gas_limit=21004,
to=binascii.unhexlify('8ea7a3fccc211ed48b763b4164884ddbcf3b0a98'),
value=0,
data='\0',
chain_id=3)
self.assertEqual(sig_v, 42)
self.assertEqual(binascii.hexlify(sig_r), 'dd52f026972a83c56b7dea356836fcfc70a68e3b879cdc8ef2bb5fea23e0a7aa')
self.assertEqual(binascii.hexlify(sig_s), '079285fe579c9a2da25c811b1c5c0a74cd19b6301ee42cf20ef7b3b1353f7242')
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
n=[0x80000000 | 44, 0x80000000 | 1, 0x80000000, 0, 0],
nonce=5,
gas_price=0,
gas_limit=21004,
to=binascii.unhexlify('8ea7a3fccc211ed48b763b4164884ddbcf3b0a98'),
value=0,
data='\0',
chain_id=3)
self.assertEqual(sig_v, 42)
self.assertEqual(binascii.hexlify(sig_r), 'f7505f709d5999343aea3c384034c62d0514336ff6c6af65582006f708f81503')
self.assertEqual(binascii.hexlify(sig_s), '44e09e29a4b6247000b46ddc94fe391e94deb2b39ad6ac6398e6db5bec095ba9')
if __name__ == '__main__':
unittest.main()

View File

@ -1,64 +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>
#
# 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/>.
import unittest
import common
import trezorlib.ckd_public as bip32
class TestMsgGetaddress(common.TrezorTest):
def test_btc(self):
self.setup_mnemonic_nopin_nopassphrase()
self.assertEqual(self.client.get_address('Bitcoin', []), '1EfKbQupktEMXf4gujJ9kCFo83k1iMqwqK')
self.assertEqual(self.client.get_address('Bitcoin', [1]), '1CK7SJdcb8z9HuvVft3D91HLpLC6KSsGb')
self.assertEqual(self.client.get_address('Bitcoin', [0, -1]), '1JVq66pzRBvqaBRFeU9SPVvg3er4ZDgoMs')
self.assertEqual(self.client.get_address('Bitcoin', [-9, 0]), '1F4YdQdL9ZQwvcNTuy5mjyQxXkyCfMcP2P')
self.assertEqual(self.client.get_address('Bitcoin', [0, 9999999]), '1GS8X3yc7ntzwGw9vXwj9wqmBWZkTFewBV')
def test_ltc(self):
self.setup_mnemonic_nopin_nopassphrase()
self.assertEqual(self.client.get_address('Litecoin', []), 'LYtGrdDeqYUQnTkr5sHT2DKZLG7Hqg7HTK')
self.assertEqual(self.client.get_address('Litecoin', [1]), 'LKRGNecThFP3Q6c5fosLVA53Z2hUDb1qnE')
self.assertEqual(self.client.get_address('Litecoin', [0, -1]), 'LcinMK8pVrAtpz7Qpc8jfWzSFsDLgLYfG6')
self.assertEqual(self.client.get_address('Litecoin', [-9, 0]), 'LZHVtcwAEDf1BR4d67551zUijyLUpDF9EX')
self.assertEqual(self.client.get_address('Litecoin', [0, 9999999]), 'Laf5nGHSCT94C5dK6fw2RxuXPiw2ZuRR9S')
def test_tbtc(self):
self.setup_mnemonic_nopin_nopassphrase()
self.assertEqual(self.client.get_address('Testnet', [111, 42]), 'moN6aN6NP1KWgnPSqzrrRPvx2x1UtZJssa')
def test_public_ckd(self):
self.setup_mnemonic_nopin_nopassphrase()
node = self.client.get_public_node([]).node
node_sub1 = self.client.get_public_node([1]).node
node_sub2 = bip32.public_ckd(node, [1])
self.assertEqual(node_sub1.chain_code, node_sub2.chain_code)
self.assertEqual(node_sub1.public_key, node_sub2.public_key)
address1 = self.client.get_address('Bitcoin', [1])
address2 = bip32.get_address(node_sub2, 0)
self.assertEqual(address2, '1CK7SJdcb8z9HuvVft3D91HLpLC6KSsGb')
self.assertEqual(address1, address2)
if __name__ == '__main__':
unittest.main()

View File

@ -1,44 +0,0 @@
import unittest
import common
import trezorlib.ckd_public as bip32
import trezorlib.types_pb2 as proto_types
class TestMsgGetaddressSegwit(common.TrezorTest):
def test_show_segwit(self):
self.setup_mnemonic_allallall()
self.assertEqual(self.client.get_address("Testnet", self.client.expand_path("49'/1'/0'/1/0"),
True, None, script_type=proto_types.SPENDP2SHWITNESS),
'2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX')
self.assertEqual(self.client.get_address("Testnet", self.client.expand_path("49'/1'/0'/0/0"),
False, None, script_type=proto_types.SPENDP2SHWITNESS),
'2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp')
self.assertEqual(self.client.get_address("Testnet", self.client.expand_path("44'/1'/0'/0/0"),
False, None, script_type=proto_types.SPENDP2SHWITNESS),
'2N6UeBoqYEEnybg4cReFYDammpsyDw8R2Mc')
self.assertEqual(self.client.get_address("Testnet", self.client.expand_path("44'/1'/0'/0/0"),
False, None, script_type=proto_types.SPENDADDRESS),
'mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q')
def test_show_multisig_3(self):
self.setup_mnemonic_allallall()
nodes = map(lambda index: self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)), range(1, 4))
multisig1 = proto_types.MultisigRedeemScriptType(
pubkeys=map(lambda n: proto_types.HDNodePathType(node=bip32.deserialize(n.xpub), address_n=[2, 0]), nodes),
signatures=[b'', b'', b''],
m=2,
)
# multisig2 = proto_types.MultisigRedeemScriptType(
# pubkeys=map(lambda n: proto_types.HDNodePathType(node=bip32.deserialize(n.xpub), address_n=[2, 1]), nodes),
# signatures=[b'', b'', b''],
# m=2,
# )
for i in [1, 2, 3]:
self.assertEqual(self.client.get_address("Testnet", self.client.expand_path("999'/1'/%d'/2/0" % i),
False, multisig1, script_type=proto_types.SPENDP2SHWITNESS),
'2N2MxyAfifVhb3AMagisxaj3uij8bfXqf4Y')
if __name__ == '__main__':
unittest.main()

View File

@ -1,47 +0,0 @@
import unittest
import common
import trezorlib.ckd_public as bip32
import trezorlib.types_pb2 as proto_types
class TestMsgGetaddressSegwitNative(common.TrezorTest):
def test_show_segwit(self):
self.setup_mnemonic_allallall()
self.assertEqual(self.client.get_address("Testnet", self.client.expand_path("49'/1'/0'/0/0"),
True, None, script_type=proto_types.SPENDWITNESS),
'QWywnqNMsMNavbCgMYiQLa91ApvsVRoaqt1i')
self.assertEqual(self.client.get_address("Testnet", self.client.expand_path("49'/1'/0'/1/0"),
False, None, script_type=proto_types.SPENDWITNESS),
'QWzGpyMkAEvmkSVprBzRRVQMP6UPp17q4kQn')
self.assertEqual(self.client.get_address("Testnet", self.client.expand_path("44'/1'/0'/0/0"),
False, None, script_type=proto_types.SPENDWITNESS),
'QWzCpc1NeTN7hNDzK9sQQ9yrTQP8zh5Hef5J')
self.assertEqual(self.client.get_address("Testnet", self.client.expand_path("44'/1'/0'/0/0"),
False, None, script_type=proto_types.SPENDADDRESS),
'mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q')
def test_show_multisig_3(self):
self.setup_mnemonic_allallall()
nodes = map(lambda index: self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)), range(1, 4))
multisig1 = proto_types.MultisigRedeemScriptType(
pubkeys=map(lambda n: proto_types.HDNodePathType(node=bip32.deserialize(n.xpub), address_n=[2, 0]), nodes),
signatures=[b'', b'', b''],
m=2,
)
multisig2 = proto_types.MultisigRedeemScriptType(
pubkeys=map(lambda n: proto_types.HDNodePathType(node=bip32.deserialize(n.xpub), address_n=[2, 1]), nodes),
signatures=[b'', b'', b''],
m=2,
)
for i in [1, 2, 3]:
self.assertEqual(self.client.get_address("Testnet", self.client.expand_path("999'/1'/%d'/2/1" % i),
False, multisig2, script_type=proto_types.SPENDWITNESS),
'T7nZJt6QbGJy6Hok4EF2LqtJPcT7z7VFSrSysGS3tEqCfDPwizqy4')
self.assertEqual(self.client.get_address("Testnet", self.client.expand_path("999'/1'/%d'/2/0" % i),
False, multisig1, script_type=proto_types.SPENDWITNESS),
'T7nY3A3kewpDKumsdhonP4TBDfTXFSc2RNhZxkqmeeszRDHjM5yUn')
if __name__ == '__main__':
unittest.main()

View File

@ -1,48 +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>
#
# 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/>.
import unittest
import common
import trezorlib.ckd_public as bip32
class TestMsgGetpublic_key(common.TrezorTest):
def test_btc(self):
self.setup_mnemonic_nopin_nopassphrase()
self.assertEqual(bip32.serialize(self.client.get_public_node([]).node, 0x0488B21E), 'xpub661MyMwAqRbcF1zGijBb2K6x9YiJPh58xpcCeLvTxMX6spkY3PcpJ4ABcCyWfskq5DDxM3e6Ez5ePCqG5bnPUXR4wL8TZWyoDaUdiWW7bKy')
self.assertEqual(bip32.serialize(self.client.get_public_node([1]).node, 0x0488B21E), 'xpub68zNxjsTrV8y9AadThLW7dTAqEpZ7xBLFSyJ3X9pjTv6Njg6kxgjXJkzxq8u3ttnjBw1jupQHMP3gpGZzZqd1eh5S4GjkaMhPR18vMyUi8N')
self.assertEqual(bip32.serialize(self.client.get_public_node([0, -1]).node, 0x0488B21E), 'xpub6A3FoZqYXj1AbW4thRwBh26YwZWbmoyjTaZwwxJjY1oKUpefLepL3RFS9DHKQrjAfxDrzDepYMDZPqXN6upQm3bHQ9xaXD5a3mqni3goF4v')
self.assertEqual(bip32.serialize(self.client.get_public_node([-9, 0]).node, 0x0488B21E), 'xpub6A2h5mzLDfYginoD7q7wCWbq18wTbN9gducRr2w5NRTwdLeoT3cJSwefFqW7uXTpVFGtpUyDMBNYs3DNvvXx6NPjF9YEbUQrtxFSWnPtVrv')
self.assertEqual(bip32.serialize(self.client.get_public_node([0, 9999999]).node, 0x0488B21E), 'xpub6A3FoZqQEK6iwLZ4HFkqSo5fb35BH4bpjC4SPZ63prfLdGYPwYxEuC6o91bUvFFdMzKWe5rs3axHRUjxJaSvBnKKFtnfLwDACRxPxabsv2r')
def test_ltc(self):
self.setup_mnemonic_nopin_nopassphrase()
self.assertEqual(bip32.serialize(self.client.get_public_node([]).node, 0x019dA462), 'Ltub2SSUS19CirucVPGDKDBatBDBEM2s9UbH66pBURfaKrMocCPLhQ7Z7hecy5VYLHA5fRdXwB2e61j2VJCNzVsqKTCVEU1vECjqi5EyczFX9xp')
self.assertEqual(bip32.serialize(self.client.get_public_node([1]).node, 0x019dA462), 'Ltub2VRVRP5VjvSyPXra4BLVyVZPv397sjhUNjBGsbtw6xko77JuQyBULxFSKheviJJ3KQLbL3Cx8P2RnudguTw4raUVjCACRG7jsumUptYx55C')
self.assertEqual(bip32.serialize(self.client.get_public_node([0, -1]).node, 0x019dA462), 'Ltub2WUNGD3aRAKAqsLqHuwBYtCn2MqAXbVsarmvn33quWe2DCHTzfK4s4jsW5oM5G8RGAdSaM3NPNrwVvtV1ourbyNhhHr3BtqcYGc8caf5GoT')
self.assertEqual(bip32.serialize(self.client.get_public_node([-9, 0]).node, 0x019dA462), 'Ltub2WToYRCN76rgyA59iK7w4Ni45wG2M9fpmBpQg7gBjvJeMiHc7473Gb96ci29Zvs55TgUQcMmCD1vy8aVqpdPwJB9YHRhGAAuPT1nRLLXmFu')
self.assertEqual(bip32.serialize(self.client.get_public_node([0, 9999999]).node, 0x019dA462), 'Ltub2WUNGD3S7kQjBhpzsjkqJfBtfqPk2r7xrUGRDdqACMW3MeBCbZSyiqbEVt7WaeesxCj6EDFQtcbfXa75DUYN2i6jZ2g81cyCgvijs9J2u2n')
def test_tbtc(self):
self.setup_mnemonic_nopin_nopassphrase()
self.assertEqual(bip32.serialize(self.client.get_public_node([111, 42]).node, 0x043587CF), 'tpubDAgixSyai5PWbc8N1mBkHDR5nLgAnHFtY7r4y5EzxqAxrt9YUDpZL3kaRoHVvCfrcwNo31c2isBP2uTHcZxEosuKbyJhCAbrvGoPuLUZ7Mz')
if __name__ == '__main__':
unittest.main()

View File

@ -1,664 +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>
#
# 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/>.
import unittest
import common
import binascii
import trezorlib.messages_pb2 as proto
import trezorlib.types_pb2 as proto_types
from trezorlib.client import CallException
from trezorlib.tx_api import TxApiTestnet
TXHASH_157041 = binascii.unhexlify(b'1570416eb4302cf52979afd5e6909e37d8fdd874301f7cc87e547e509cb1caa6')
TXHASH_39a29e = binascii.unhexlify(b'39a29e954977662ab3879c66fb251ef753e0912223a83d1dcb009111d28265e5')
TXHASH_4a7b7e = binascii.unhexlify(b'4a7b7e0403ae5607e473949cfa03f09f2cd8b0f404bf99ce10b7303d86280bf7')
TXHASH_54aa56 = binascii.unhexlify(b'54aa5680dea781f45ebb536e53dffc526d68c0eb5c00547e323b2c32382dfba3')
TXHASH_58497a = binascii.unhexlify(b'58497a7757224d1ff1941488d23087071103e5bf855f4c1c44e5c8d9d82ca46e')
TXHASH_6f90f3 = binascii.unhexlify(b'6f90f3c7cbec2258b0971056ef3fe34128dbde30daa9c0639a898f9977299d54')
TXHASH_c63e24 = binascii.unhexlify(b'c63e24ed820c5851b60c54613fbc4bcb37df6cd49b4c96143e99580a472f79fb')
TXHASH_c6be22 = binascii.unhexlify(b'c6be22d34946593bcad1d2b013e12f74159e69574ffea21581dad115572e031c')
TXHASH_d5f65e = binascii.unhexlify(b'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')
TXHASH_d6da21 = binascii.unhexlify(b'd6da21677d7cca5f42fbc7631d062c9ae918a0254f7c6c22de8e8cb7fd5b8236')
class TestMsgSigntx(common.TrezorTest):
def test_one_one_fee(self):
self.setup_mnemonic_nopin_nopassphrase()
# tx: d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882
# input 0: 0.0039 BTC
inp1 = proto_types.TxInputType(
address_n=[0], # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e
# amount=390000,
prev_hash=TXHASH_d5f65e,
prev_index=0,
)
out1 = proto_types.TxOutputType(
address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
amount=390000 - 10000,
script_type=proto_types.PAYTOADDRESS,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin', [inp1, ], [out1, ])
# Accepted by network: tx fd79435246dee76b2f159d2db08032d666c95adc544de64c8c49f474df4a7fee
self.assertEqual(binascii.hexlify(serialized_tx), b'010000000182488650ef25a58fef6788bd71b8212038d7f2bbe4750bc7bcb44701e85ef6d5000000006b4830450221009a0b7be0d4ed3146ee262b42202841834698bb3ee39c24e7437df208b8b7077102202b79ab1e7736219387dffe8d615bbdba87e11477104b867ef47afed1a5ede7810121023230848585885f63803a0a8aecdd6538792d5c539215c91698e315bf0253b43dffffffff0160cc0500000000001976a914de9b2a8da088824e8fe51debea566617d851537888ac00000000')
def test_testnet_one_two_fee(self):
self.setup_mnemonic_nopin_nopassphrase()
# tx: 6f90f3c7cbec2258b0971056ef3fe34128dbde30daa9c0639a898f9977299d54
# input 1: 10.00000000 BTC
inp1 = proto_types.TxInputType(
address_n=[0], # mirio8q3gtv7fhdnmb3TpZ4EuafdzSs7zL
# amount=1000000000,
prev_hash=TXHASH_6f90f3,
prev_index=1,
)
out1 = proto_types.TxOutputType(
address='mfiGQVPcRcaEvQPYDErR34DcCovtxYvUUV',
amount=1000000000 - 500000000 - 10000000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address_n=[2],
amount=500000000,
script_type=proto_types.PAYTOADDRESS,
)
with self.client:
self.client.set_tx_api(TxApiTestnet)
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_6f90f3)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_6f90f3)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_6f90f3)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_6f90f3)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_6f90f3)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Testnet', [inp1, ], [out1, out2])
self.assertEqual(binascii.hexlify(serialized_tx), b'0100000001549d2977998f899a63c0a9da30dedb2841e33fef561097b05822eccbc7f3906f010000006b4830450221009c2d30385519fdb13dce13d5ac038be07d7b2dad0b0f7b2c1c339d7255bcf553022056a2f5bceab3cd0ffed4d388387e631f419d67ff9ce7798e3d7dfe6a6d6ec4bd0121023230848585885f63803a0a8aecdd6538792d5c539215c91698e315bf0253b43dffffffff0280ce341d000000001976a9140223b1a09138753c9cb0baf95a0a62c82711567a88ac0065cd1d000000001976a9142db345c36563122e2fd0f5485fb7ea9bbf7cb5a288ac00000000')
def test_testnet_fee_too_high(self):
self.setup_mnemonic_nopin_nopassphrase()
# tx: 6f90f3c7cbec2258b0971056ef3fe34128dbde30daa9c0639a898f9977299d54
# input 1: 10.00000000 BTC
inp1 = proto_types.TxInputType(
address_n=[0], # mirio8q3gtv7fhdnmb3TpZ4EuafdzSs7zL
# amount=1000000000,
prev_hash=TXHASH_6f90f3,
prev_index=1,
)
out1 = proto_types.TxOutputType(
address='mfiGQVPcRcaEvQPYDErR34DcCovtxYvUUV',
amount=1000000000 - 500000000 - 100000000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address_n=[2],
amount=500000000,
script_type=proto_types.PAYTOADDRESS,
)
with self.client:
self.client.set_tx_api(TxApiTestnet)
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_6f90f3)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_6f90f3)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_6f90f3)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_6f90f3)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_6f90f3)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_FeeOverThreshold),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Testnet', [inp1, ], [out1, out2])
self.assertEqual(binascii.hexlify(serialized_tx), b'0100000001549d2977998f899a63c0a9da30dedb2841e33fef561097b05822eccbc7f3906f010000006a47304402205ea68e9d52d4be14420ccecf7f2e11489d49b86bedb79ee99b5e9b7188884150022056219cb3384a5df8048cca286a9533403dbda1571afd84b51379cdaee6a6dea80121023230848585885f63803a0a8aecdd6538792d5c539215c91698e315bf0253b43dffffffff020084d717000000001976a9140223b1a09138753c9cb0baf95a0a62c82711567a88ac0065cd1d000000001976a9142db345c36563122e2fd0f5485fb7ea9bbf7cb5a288ac00000000')
def test_one_two_fee(self):
self.setup_mnemonic_nopin_nopassphrase()
# tx: d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882
# input 0: 0.0039 BTC
inp1 = proto_types.TxInputType(
address_n=[0], # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e
# amount=390000,
prev_hash=TXHASH_d5f65e,
prev_index=0,
)
out1 = proto_types.TxOutputType(
address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
amount=390000 - 80000 - 10000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address_n=[1],
amount=80000,
script_type=proto_types.PAYTOADDRESS,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin', [inp1, ], [out1, out2])
self.assertEqual(binascii.hexlify(serialized_tx), b'010000000182488650ef25a58fef6788bd71b8212038d7f2bbe4750bc7bcb44701e85ef6d5000000006b483045022100c1400d8485d3bdcae7413e123148f35ece84806fc387ab88c66b469df89aef1702201d481d04216b319dc549ffe2333143629ba18828a4e2d1783ab719a6aa263eb70121023230848585885f63803a0a8aecdd6538792d5c539215c91698e315bf0253b43dffffffff02e0930400000000001976a914de9b2a8da088824e8fe51debea566617d851537888ac80380100000000001976a9140223b1a09138753c9cb0baf95a0a62c82711567a88ac00000000')
def test_one_three_fee(self):
self.setup_mnemonic_nopin_nopassphrase()
# tx: d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882
# input 0: 0.0039 BTC
inp1 = proto_types.TxInputType(
address_n=[0], # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e
# amount=390000,
prev_hash=TXHASH_d5f65e,
prev_index=0,
)
out1 = proto_types.TxOutputType(
address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
amount=390000 - 80000 - 12000 - 10000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address='13uaUYn6XAooo88QvAqAVsiVvr2mAXutqP',
amount=12000,
script_type=proto_types.PAYTOADDRESS,
)
out3 = proto_types.TxOutputType(
address_n=[1],
amount=80000,
script_type=proto_types.PAYTOADDRESS,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=2)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=2)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=2)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin', [inp1, ], [out1, out2, out3])
self.assertEqual(binascii.hexlify(serialized_tx), b'010000000182488650ef25a58fef6788bd71b8212038d7f2bbe4750bc7bcb44701e85ef6d5000000006b483045022100e695e2c530c7c0fc32e6b79b7cff56a7f70a8c9da787534f46b4204070f914fc02207b0879a81408a11e23b11d4c7965c62b5fc6d5c2d92340f5ee2da7b40e99314a0121023230848585885f63803a0a8aecdd6538792d5c539215c91698e315bf0253b43dffffffff0300650400000000001976a914de9b2a8da088824e8fe51debea566617d851537888ace02e0000000000001976a9141fe1d337fb81afca42818051e12fd18245d1b17288ac80380100000000001976a9140223b1a09138753c9cb0baf95a0a62c82711567a88ac00000000')
def test_two_two(self):
self.setup_mnemonic_nopin_nopassphrase()
# tx: c6be22d34946593bcad1d2b013e12f74159e69574ffea21581dad115572e031c
# input 1: 0.0010 BTC
# tx: 58497a7757224d1ff1941488d23087071103e5bf855f4c1c44e5c8d9d82ca46e
# input 1: 0.0011 BTC
inp1 = proto_types.TxInputType(
address_n=[1], # 1CK7SJdcb8z9HuvVft3D91HLpLC6KSsGb
# amount=100000,
prev_hash=TXHASH_c6be22,
prev_index=1,
)
inp2 = proto_types.TxInputType(
address_n=[2], # 15AeAhtNJNKyowK8qPHwgpXkhsokzLtUpG
# amount=110000,
prev_hash=TXHASH_58497a,
prev_index=1,
)
out1 = proto_types.TxOutputType(
address='15Jvu3nZNP7u2ipw2533Q9VVgEu2Lu9F2B',
amount=210000 - 100000 - 10000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address_n=[3], # 1CmzyJp9w3NafXMSEFH4SLYUPAVCSUrrJ5
amount=100000,
script_type=proto_types.PAYTOADDRESS,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_c6be22)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_c6be22)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_c6be22)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_c6be22)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_58497a)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_58497a)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_58497a)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_58497a)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin', [inp1, inp2], [out1, out2])
# Accepted by network: tx c63e24ed820c5851b60c54613fbc4bcb37df6cd49b4c96143e99580a472f79fb
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000021c032e5715d1da8115a2fe4f57699e15742fe113b0d2d1ca3b594649d322bec6010000006b483045022100f773c403b2f85a5c1d6c9c4ad69c43de66930fff4b1bc818eb257af98305546a0220443bde4be439f276a6ce793664b463580e210ec6c9255d68354449ac0443c76501210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6ffffffff6ea42cd8d9c8e5441c4c5f85bfe50311078730d2881494f11f4d2257777a4958010000006b48304502210090cff1c1911e771605358a8cddd5ae94c7b60cc96e50275908d9bf9d6367c79f02202bfa72e10260a146abd59d0526e1335bacfbb2b4401780e9e3a7441b0480c8da0121038caebd6f753bbbd2bb1f3346a43cd32140648583673a31d62f2dfb56ad0ab9e3ffffffff02a0860100000000001976a9142f4490d5263906e4887ca2996b9e207af3e7824088aca0860100000000001976a914812c13d97f9159e54e326b481b8f88a73df8507a88ac00000000')
"""
def test_lots_of_inputs(self):
self.setup_mnemonic_nopin_nopassphrase()
# Tests if device implements serialization of len(inputs) correctly
# tx 4a7b7e0403ae5607e473949cfa03f09f2cd8b0f404bf99ce10b7303d86280bf7 : 100 UTXO for spending for unittests
inputs = []
for i in range(100):
inputs.append( proto_types.TxInputType(address_n=[4], # 1NwN6UduuVkJi6sw3gSiKZaCY5rHgVXC2h
prev_hash=TXHASH_4a7b7e,
prev_index=i) )
out = proto_types.TxOutputType(address='19dvDdyxxptP9dGvozYe8BP6tgFV9L4jg5',
amount=100 * 26000 - 15 * 10000,
script_type=proto_types.PAYTOADDRESS)
with self.client:
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin', inputs, [out])
# Accepted by network: tx 23d9d8eecf3abf6c0f0f3f8b0976a04792d7f1c9a4ea9b0a8931734949e27c92
# too big put in unit test
"""
def test_lots_of_outputs(self):
self.setup_mnemonic_nopin_nopassphrase()
# Tests if device implements serialization of len(outputs) correctly
# tx: c63e24ed820c5851b60c54613fbc4bcb37df6cd49b4c96143e99580a472f79fb
# index 1: 0.0010 BTC
# tx: 39a29e954977662ab3879c66fb251ef753e0912223a83d1dcb009111d28265e5
# index 1: 0.0254 BTC
inp1 = proto_types.TxInputType(
address_n=[3], # 1CmzyJp9w3NafXMSEFH4SLYUPAVCSUrrJ5
# amount=100000,
prev_hash=TXHASH_c63e24,
prev_index=1,
)
inp2 = proto_types.TxInputType(
address_n=[3], # 1CmzyJp9w3NafXMSEFH4SLYUPAVCSUrrJ5
# amount=2540000,
prev_hash=TXHASH_39a29e,
prev_index=1,
)
outputs = []
cnt = 255
for _ in range(cnt):
out = proto_types.TxOutputType(
address='1NwN6UduuVkJi6sw3gSiKZaCY5rHgVXC2h',
amount=(100000 + 2540000 - 39000) // cnt,
script_type=proto_types.PAYTOADDRESS,
)
outputs.append(out)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_c63e24)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_c63e24)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_c63e24)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_c63e24)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_c63e24)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_39a29e)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_39a29e)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_39a29e)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_39a29e)),
] + [
item for items in zip(
[proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=I)) for I in range(cnt)],
[proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput)] * cnt
) for item in items
] + [
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
] + [
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=I)) for I in range(cnt)
] + [
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
] + [
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=I)) for I in range(cnt)
] + [
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=I)) for I in range(cnt)
] + [
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin', [inp1, inp2], outputs)
if cnt == 255:
self.assertEqual(binascii.hexlify(serialized_tx), b'0100000002fb792f470a58993e14964c9bd46cdf37cb4bbc3f61540cb651580c82ed243ec6010000006b483045022100969da46f94a81f34f3717b014e0c3e1826eda1b0022ec2f9ce39f3d750ab9235022026da269770993211a1503413566a339bbb4389a482fffcf8e1f76713fc3b94f5012103477b9f0f34ae85434ce795f0c5e1e90c9420e5b5fad084d7cce9a487b94a7902ffffffffe56582d2119100cb1d3da8232291e053f71e25fb669c87b32a667749959ea239010000006a473044022052e1419bb237b9db400ab5e3df16db6355619d545fde9030924a360763ae9ad40220704beab04d72ecaeb42eca7d98faca7a0941e65f2e1341f183be2b83e6b09e1c012103477b9f0f34ae85434ce795f0c5e1e90c9420e5b5fad084d7cce9a487b94a7902fffffffffdff00' + b'd8270000000000001976a914f0a2b64e56ee2ff57126232f84af6e3a41d4055088ac' * cnt + b'00000000')
def test_fee_too_high(self):
self.setup_mnemonic_nopin_nopassphrase()
# tx: 1570416eb4302cf52979afd5e6909e37d8fdd874301f7cc87e547e509cb1caa6
# input 0: 1.0 BTC
inp1 = proto_types.TxInputType(
address_n=[0], # 1HWDaLTpTCTtRWyWqZkzWx1wex5NKyncLW
# amount=100000000,
prev_hash=TXHASH_157041,
prev_index=0,
)
out1 = proto_types.TxOutputType(
address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
amount=100000000 - 510000,
script_type=proto_types.PAYTOADDRESS,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_157041)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_157041)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_157041)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_157041)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_FeeOverThreshold),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin', [inp1, ], [out1, ])
self.assertEqual(binascii.hexlify(serialized_tx), b'0100000001a6cab19c507e547ec87c1f3074d8fdd8379e90e6d5af7929f52c30b46e417015000000006b483045022100dc3531da7feb261575f03b5b9bbb35edc7f73bb081c92538827105de4102737002200161e34395f6a8ee93979200cb974fa75ccef6d7c14021511cf468eece90d6450121023230848585885f63803a0a8aecdd6538792d5c539215c91698e315bf0253b43dffffffff01d018ee05000000001976a914de9b2a8da088824e8fe51debea566617d851537888ac00000000')
def test_not_enough_funds(self):
self.setup_mnemonic_nopin_nopassphrase()
# tx: d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882
# input 0: 0.0039 BTC
inp1 = proto_types.TxInputType(
address_n=[0], # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e
# amount=390000,
prev_hash=TXHASH_d5f65e,
prev_index=0,
)
out1 = proto_types.TxOutputType(
address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
amount=400000,
script_type=proto_types.PAYTOADDRESS,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_d5f65e)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.Failure(code=proto_types.Failure_NotEnoughFunds)
])
try:
self.client.sign_tx('Bitcoin', [inp1, ], [out1, ])
except CallException as e:
self.assertEqual(e.args[0], proto_types.Failure_NotEnoughFunds)
else:
self.assert_(False, "types.Failure_NotEnoughFunds expected")
def test_p2sh(self):
self.setup_mnemonic_nopin_nopassphrase()
inp1 = proto_types.TxInputType(
address_n=[0], # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e
# amount=400000,
prev_hash=TXHASH_54aa56,
prev_index=1,
)
out1 = proto_types.TxOutputType(
address='3DKGE1pvPpBAgZj94MbCinwmksewUNNYVR', # p2sh
amount=400000 - 10000,
script_type=proto_types.PAYTOSCRIPTHASH,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_54aa56)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_54aa56)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_54aa56)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=TXHASH_54aa56)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin', [inp1, ], [out1, ])
# Accepted by network: tx 8cc1f4adf7224ce855cf535a5104594a0004cb3b640d6714fdb00b9128832dd5
self.assertEqual(binascii.hexlify(serialized_tx), b'0100000001a3fb2d38322c3b327e54005cebc0686d52fcdf536e53bb5ef481a7de8056aa54010000006b4830450221009e020b0390ccad533b73b552f8a99a9d827212c558e4f755503674d07c92ad4502202d606f7316990e0461c51d4add25054f19c697aa3e3c2ced4d568f0b2c57e62f0121023230848585885f63803a0a8aecdd6538792d5c539215c91698e315bf0253b43dffffffff0170f305000000000017a9147f844bdb0b8fd54b64e3d16c85dc1170f1ff97c18700000000')
def test_attack_change_outputs(self):
# This unit test attempts to modify data sent during ping-pong of streaming signing.
# Because device is asking for human confirmation only during first pass (first input),
# device must detect that data has been modified during other passes and fail to sign
# such modified data (which has not been confirmed by the user).
# Test firstly prepare normal transaction and send it to device. Then it send the same
# transaction again, but change amount of output 1 during signing the second input.
self.setup_mnemonic_nopin_nopassphrase()
inp1 = proto_types.TxInputType(
address_n=[1], # 1CK7SJdcb8z9HuvVft3D91HLpLC6KSsGb
# amount=100000,
prev_hash=TXHASH_c6be22,
prev_index=1,
)
inp2 = proto_types.TxInputType(
address_n=[2], # 15AeAhtNJNKyowK8qPHwgpXkhsokzLtUpG
# amount=110000,
prev_hash=TXHASH_58497a,
prev_index=1,
)
out1 = proto_types.TxOutputType(
address='15Jvu3nZNP7u2ipw2533Q9VVgEu2Lu9F2B',
amount=210000 - 100000 - 10000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address_n=[3], # 1CmzyJp9w3NafXMSEFH4SLYUPAVCSUrrJ5
amount=100000,
script_type=proto_types.PAYTOADDRESS,
)
global run_attack
run_attack = False
def attack_processor(req, msg):
global run_attack
if req.details.tx_hash != b'':
return msg
if req.details.request_index != 1:
return msg
if not run_attack:
run_attack = True
return msg
msg.outputs[0].amount = 9999999 # Sign output with another amount
return msg
# Test if the transaction can be signed normally
(_, serialized_tx) = self.client.sign_tx('Bitcoin', [inp1, inp2], [out1, out2])
# Accepted by network: tx c63e24ed820c5851b60c54613fbc4bcb37df6cd49b4c96143e99580a472f79fb
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000021c032e5715d1da8115a2fe4f57699e15742fe113b0d2d1ca3b594649d322bec6010000006b483045022100f773c403b2f85a5c1d6c9c4ad69c43de66930fff4b1bc818eb257af98305546a0220443bde4be439f276a6ce793664b463580e210ec6c9255d68354449ac0443c76501210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6ffffffff6ea42cd8d9c8e5441c4c5f85bfe50311078730d2881494f11f4d2257777a4958010000006b48304502210090cff1c1911e771605358a8cddd5ae94c7b60cc96e50275908d9bf9d6367c79f02202bfa72e10260a146abd59d0526e1335bacfbb2b4401780e9e3a7441b0480c8da0121038caebd6f753bbbd2bb1f3346a43cd32140648583673a31d62f2dfb56ad0ab9e3ffffffff02a0860100000000001976a9142f4490d5263906e4887ca2996b9e207af3e7824088aca0860100000000001976a914812c13d97f9159e54e326b481b8f88a73df8507a88ac00000000')
# Now run the attack, must trigger the exception
self.assertRaises(CallException, self.client.sign_tx, 'Bitcoin', [inp1, inp2], [out1, out2], debug_processor=attack_processor)
def test_spend_coinbase(self):
# 25 TEST generated to m/1 (mfiGQVPcRcaEvQPYDErR34DcCovtxYvUUV)
# tx: d6da21677d7cca5f42fbc7631d062c9ae918a0254f7c6c22de8e8cb7fd5b8236
# input 0: 25.0027823 BTC
self.setup_mnemonic_nopin_nopassphrase()
inp1 = proto_types.TxInputType(
address_n=[1], # mfiGQVPcRcaEvQPYDErR34DcCovtxYvUUV
# amount=390000,
prev_hash=TXHASH_d6da21,
prev_index=0,
)
out1 = proto_types.TxOutputType(
address='mm6FM31rM5Vc3sw5D7kztiBg3jHUzyqF1g',
amount=2500278230 - 10000,
script_type=proto_types.PAYTOADDRESS,
)
with self.client:
self.client.set_tx_api(TxApiTestnet)
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=TXHASH_d6da21)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_d6da21)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=TXHASH_d6da21)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Testnet', [inp1, ], [out1, ])
# Accepted by network: tx
self.assertEqual(binascii.hexlify(serialized_tx), b'010000000136825bfdb78c8ede226c7c4f25a018e99a2c061d63c7fb425fca7c7d6721dad6000000006a473044022047845c366eb24f40be315c7815a154513c444c7989eb80f7ce7ff6aeb703d26a022007c1f5efadf67c5889634fd7ac39a7ce78bffac291673e8772ecd8389c901d9f01210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6ffffffff01c6100795000000001976a9143d2496e67f5f57a924353da42d4725b318e7a8ea88ac00000000')
if __name__ == '__main__':
unittest.main()

View File

@ -1,162 +0,0 @@
# This file is part of the TREZOR project.
#
# Copyright (C) 2017 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/>.
import unittest
import common
import binascii
import trezorlib.messages_pb2 as proto
import trezorlib.types_pb2 as proto_types
from trezorlib.tx_api import TxApiTestnet
from trezorlib.ckd_public import deserialize
class TestMsgSigntxSegwit(common.TrezorTest):
def test_send_p2sh(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("49'/1'/0'/1/0"),
# 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX
amount=123456789,
prev_hash=binascii.unhexlify('20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'),
prev_index=0,
script_type=proto_types.SPENDP2SHWITNESS,
)
out1 = proto_types.TxOutputType(
address='mhRx1CeVfaayqRwq5zgRQmD7W5aWBfD5mC',
amount=12300000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address='2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX',
script_type=proto_types.PAYTOADDRESS,
amount=123456789 - 11000 - 12300000,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Testnet', [inp1], [out1, out2])
self.assertEqual(binascii.hexlify(serialized_tx), b'0100000000010137c361fb8f2d9056ba8c98c5611930fcb48cacfdd0fe2e0449d83eea982f91200000000017160014d16b8c0680c61fc6ed2e407455715055e41052f5ffffffff02e0aebb00000000001976a91414fdede0ddc3be652a0ce1afbc1b509a55b6b94888ac3df39f060000000017a91458b53ea7f832e8f096e896b8713a8c6df0e892ca8702483045022100ccd253bfdf8a5593cd7b6701370c531199f0f05a418cd547dfc7da3f21515f0f02203fa08a0753688871c220648f9edadbdb98af42e5d8269364a326572cf703895b012103e7bfe10708f715e8538c92d46ca50db6f657bbc455b7494e6a0303ccdb868b7900000000')
def test_send_p2sh_change(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("49'/1'/0'/1/0"),
# 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX
amount=123456789,
prev_hash=binascii.unhexlify('20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'),
prev_index=0,
script_type=proto_types.SPENDP2SHWITNESS,
)
out1 = proto_types.TxOutputType(
address='mhRx1CeVfaayqRwq5zgRQmD7W5aWBfD5mC',
amount=12300000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address_n=self.client.expand_path("49'/1'/0'/1/0"),
script_type=proto_types.PAYTOP2SHWITNESS,
amount=123456789 - 11000 - 12300000,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Testnet', [inp1], [out1, out2])
self.assertEqual(binascii.hexlify(serialized_tx), b'0100000000010137c361fb8f2d9056ba8c98c5611930fcb48cacfdd0fe2e0449d83eea982f91200000000017160014d16b8c0680c61fc6ed2e407455715055e41052f5ffffffff02e0aebb00000000001976a91414fdede0ddc3be652a0ce1afbc1b509a55b6b94888ac3df39f060000000017a91458b53ea7f832e8f096e896b8713a8c6df0e892ca8702483045022100ccd253bfdf8a5593cd7b6701370c531199f0f05a418cd547dfc7da3f21515f0f02203fa08a0753688871c220648f9edadbdb98af42e5d8269364a326572cf703895b012103e7bfe10708f715e8538c92d46ca50db6f657bbc455b7494e6a0303ccdb868b7900000000')
def test_send_multisig_1(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
nodes = map(lambda index: self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)), range(1, 4))
multisig = proto_types.MultisigRedeemScriptType(
pubkeys=map(lambda n: proto_types.HDNodePathType(node=deserialize(n.xpub), address_n=[2, 0]), nodes),
signatures=[b'', b'', b''],
m=2,
)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("999'/1'/1'/2/0"),
prev_hash=binascii.unhexlify('9c31922be756c06d02167656465c8dc83bb553bf386a3f478ae65b5c021002be'),
prev_index=1,
script_type=proto_types.SPENDP2SHWITNESS,
multisig=multisig,
amount=1610436
)
out1 = proto_types.TxOutputType(address='mhRx1CeVfaayqRwq5zgRQmD7W5aWBfD5mC',
amount=1605000,
script_type=proto_types.PAYTOADDRESS)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures1, _) = self.client.sign_tx('Testnet', [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_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures2, serialized_tx) = self.client.sign_tx('Testnet', [inp1], [out1])
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000000101be0210025c5be68a473f6a38bf53b53bc88d5c46567616026dc056e72b92319c01000000232200201e8dda334f11171190b3da72e526d441491464769679a319a2f011da5ad312a1ffffffff01887d1800000000001976a91414fdede0ddc3be652a0ce1afbc1b509a55b6b94888ac040047304402205b44c20cf2681690edaaf7cd2e30d4704124dd8b7eb1fb7f459d3906c3c374a602205ca359b6544ce2c101c979899c782f7d141c3b0454ea69202b1fb4c09d3b715701473044022052fafa64022554ae436dbf781e550bf0d326fef31eea1438350b3ff1940a180102202851bd19203b7fe8582a9ef52e82aa9f61cd52d4bcedfe6dcc0cf782468e6a8e01695221038e81669c085a5846e68e03875113ddb339ecbb7cb11376d4163bca5dc2e2a0c1210348c5c3be9f0e6cf1954ded1c0475beccc4d26aaa9d0cce2dd902538ff1018a112103931140ebe0fbbb7df0be04ed032a54e9589e30339ba7bbb8b0b71b15df1294da53ae00000000')
if __name__ == '__main__':
unittest.main()

View File

@ -1,482 +0,0 @@
# This file is part of the TREZOR project.
#
# Copyright (C) 2017 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/>.
import unittest
import common
import binascii
import trezorlib.messages_pb2 as proto
import trezorlib.types_pb2 as proto_types
from trezorlib.tx_api import TxApiTestnet
from trezorlib.ckd_public import deserialize
class TestMsgSigntxSegwit(common.TrezorTest):
def test_send_p2sh(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("49'/1'/0'/1/0"),
# 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX
amount=123456789,
prev_hash=binascii.unhexlify('20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'),
prev_index=0,
script_type=proto_types.SPENDP2SHWITNESS,
)
out1 = proto_types.TxOutputType(
address='QWywnqNMsMNavbCgMYiQLa91ApvsVRoaqt1i',
amount=12300000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address='2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX',
script_type=proto_types.PAYTOADDRESS,
amount=123456789 - 11000 - 12300000,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Testnet', [inp1], [out1, out2])
self.assertEqual(binascii.hexlify(serialized_tx), b'0100000000010137c361fb8f2d9056ba8c98c5611930fcb48cacfdd0fe2e0449d83eea982f91200000000017160014d16b8c0680c61fc6ed2e407455715055e41052f5ffffffff02e0aebb00000000001600140099a7ecbd938ed1839f5f6bf6d50933c6db9d5c3df39f060000000017a91458b53ea7f832e8f096e896b8713a8c6df0e892ca8702483045022100bd3d8b8ad35c094e01f6282277300e575f1021678fc63ec3f9945d6e35670da3022052e26ef0dd5f3741c9d5939d1dec5464c15ab5f2c85245e70a622df250d4eb7c012103e7bfe10708f715e8538c92d46ca50db6f657bbc455b7494e6a0303ccdb868b7900000000')
def test_send_p2sh_change(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("49'/1'/0'/1/0"),
# 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX
amount=123456789,
prev_hash=binascii.unhexlify('20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'),
prev_index=0,
script_type=proto_types.SPENDP2SHWITNESS,
)
out1 = proto_types.TxOutputType(
address='QWywnqNMsMNavbCgMYiQLa91ApvsVRoaqt1i',
amount=12300000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address_n=self.client.expand_path("49'/1'/0'/1/0"),
script_type=proto_types.PAYTOP2SHWITNESS,
amount=123456789 - 11000 - 12300000,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Testnet', [inp1], [out1, out2])
self.assertEqual(binascii.hexlify(serialized_tx), b'0100000000010137c361fb8f2d9056ba8c98c5611930fcb48cacfdd0fe2e0449d83eea982f91200000000017160014d16b8c0680c61fc6ed2e407455715055e41052f5ffffffff02e0aebb00000000001600140099a7ecbd938ed1839f5f6bf6d50933c6db9d5c3df39f060000000017a91458b53ea7f832e8f096e896b8713a8c6df0e892ca8702483045022100bd3d8b8ad35c094e01f6282277300e575f1021678fc63ec3f9945d6e35670da3022052e26ef0dd5f3741c9d5939d1dec5464c15ab5f2c85245e70a622df250d4eb7c012103e7bfe10708f715e8538c92d46ca50db6f657bbc455b7494e6a0303ccdb868b7900000000')
def test_send_native(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("49'/1'/0'/0/0"),
# QWywnqNMsMNavbCgMYiQLa91ApvsVRoaqt1i
amount=12300000,
prev_hash=binascii.unhexlify('09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'),
prev_index=0,
script_type=proto_types.SPENDWITNESS,
)
out1 = proto_types.TxOutputType(
address='2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp',
amount=5000000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address='QWzGpyMkAEvmkSVprBzRRVQMP6UPp17q4kQn',
script_type=proto_types.PAYTOADDRESS,
amount=12300000 - 11000 - 5000000,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Testnet', [inp1], [out1, out2])
self.assertEqual(binascii.hexlify(serialized_tx), b'010000000001018a44999c07bba32df1cacdc50987944e68e3205b4429438fdde35c76024614090000000000ffffffff02404b4c000000000017a9147a55d61848e77ca266e79a39bfc85c580a6426c987a8386f0000000000160014d16b8c0680c61fc6ed2e407455715055e41052f502483045022100a7ca8f097525f9044e64376dc0a0f5d4aeb8d15d66808ba97979a0475b06b66502200597c8ebcef63e047f9aeef1a8001d3560470cf896c12f6990eec4faec599b950121033add1f0e8e3c3136f7428dd4a4de1057380bd311f5b0856e2269170b4ffa65bf00000000')
def test_send_native_change(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("49'/1'/0'/0/0"),
# QWywnqNMsMNavbCgMYiQLa91ApvsVRoaqt1i
amount=12300000,
prev_hash=binascii.unhexlify('09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'),
prev_index=0,
script_type=proto_types.SPENDWITNESS,
)
out1 = proto_types.TxOutputType(
address='2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp',
amount=5000000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
address_n=self.client.expand_path("49'/1'/0'/1/0"),
script_type=proto_types.PAYTOWITNESS,
amount=12300000 - 11000 - 5000000,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Testnet', [inp1], [out1, out2])
self.assertEqual(binascii.hexlify(serialized_tx), b'010000000001018a44999c07bba32df1cacdc50987944e68e3205b4429438fdde35c76024614090000000000ffffffff02404b4c000000000017a9147a55d61848e77ca266e79a39bfc85c580a6426c987a8386f0000000000160014d16b8c0680c61fc6ed2e407455715055e41052f502483045022100a7ca8f097525f9044e64376dc0a0f5d4aeb8d15d66808ba97979a0475b06b66502200597c8ebcef63e047f9aeef1a8001d3560470cf896c12f6990eec4faec599b950121033add1f0e8e3c3136f7428dd4a4de1057380bd311f5b0856e2269170b4ffa65bf00000000')
def test_send_both(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("49'/1'/0'/1/0"),
# 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX
amount=111145789,
prev_hash=binascii.unhexlify('09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'),
prev_index=1,
script_type=proto_types.SPENDP2SHWITNESS,
)
inp2 = proto_types.TxInputType(
address_n=self.client.expand_path("49'/1'/0'/1/0"),
# QWzGpyMkAEvmkSVprBzRRVQMP6UPp17q4kQn
amount=7289000,
prev_hash=binascii.unhexlify('65b811d3eca0fe6915d9f2d77c86c5a7f19bf66b1b1253c2c51cb4ae5f0c017b'),
prev_index=1,
script_type=proto_types.SPENDWITNESS,
)
out1 = proto_types.TxOutputType(
address='QWzCpc1NeTN7hNDzK9sQQ9yrTQP8zh5Hef5J',
amount=12300000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
# address_n=self.client.expand_path("44'/1'/0'/0/0"),
# script_type=proto_types.PAYTOP2SHWITNESS,
address='2N6UeBoqYEEnybg4cReFYDammpsyDw8R2Mc',
script_type=proto_types.PAYTOADDRESS,
amount=45600000,
)
out3 = proto_types.TxOutputType(
address='mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q',
amount=111145789 + 7289000 - 11000 - 12300000 - 45600000,
script_type=proto_types.PAYTOADDRESS,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=2)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=2)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Testnet', [inp1, inp2], [out1, out2, out3])
# 0e480a97c7a545c85e101a2f13c9af0e115d43734e1448f0cac3e55fe8e7399d
self.assertEqual(binascii.hexlify(serialized_tx), b'010000000001028a44999c07bba32df1cacdc50987944e68e3205b4429438fdde35c76024614090100000017160014d16b8c0680c61fc6ed2e407455715055e41052f5ffffffff7b010c5faeb41cc5c253121b6bf69bf1a7c5867cd7f2d91569fea0ecd311b8650100000000ffffffff03e0aebb0000000000160014a579388225827d9f2fe9014add644487808c695d00cdb7020000000017a91491233e24a9bf8dbb19c1187ad876a9380c12e787870d859b03000000001976a914a579388225827d9f2fe9014add644487808c695d88ac02483045022100ead79ee134f25bb585b48aee6284a4bb14e07f03cc130253e83450d095515e5202201e161e9402c8b26b666f2b67e5b668a404ef7e57858ae9a6a68c3837e65fdc69012103e7bfe10708f715e8538c92d46ca50db6f657bbc455b7494e6a0303ccdb868b7902483045022100b4099ec4c7b3123795b3c080a86f4b745f3784eb3f77de79bef1d8da319cbee5022039766865d448a4a3e435a95d0df3ff56ebc6532bf538988a7e8a679b40ec41b6012103e7bfe10708f715e8538c92d46ca50db6f657bbc455b7494e6a0303ccdb868b7900000000')
def test_send_multisig_1(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
nodes = map(lambda index: self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)), range(1, 4))
multisig = proto_types.MultisigRedeemScriptType(
pubkeys=map(lambda n: proto_types.HDNodePathType(node=deserialize(n.xpub), address_n=[2, 0]), nodes),
signatures=[b'', b'', b''],
m=2,
)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("999'/1'/1'/2/0"),
prev_hash=binascii.unhexlify('9c31922be756c06d02167656465c8dc83bb553bf386a3f478ae65b5c021002be'),
prev_index=1,
script_type=proto_types.SPENDP2SHWITNESS,
multisig=multisig,
amount=1610436
)
out1 = proto_types.TxOutputType(
address='T7nZJt6QbGJy6Hok4EF2LqtJPcT7z7VFSrSysGS3tEqCfDPwizqy4',
amount=1605000,
script_type=proto_types.PAYTOADDRESS
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures1, _) = self.client.sign_tx('Testnet', [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_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures2, serialized_tx) = self.client.sign_tx('Testnet', [inp1], [out1])
# f41cbedd8becee05a830f418d13aa665125464547db5c7a6cd28f21639fe1228
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000000101be0210025c5be68a473f6a38bf53b53bc88d5c46567616026dc056e72b92319c01000000232200201e8dda334f11171190b3da72e526d441491464769679a319a2f011da5ad312a1ffffffff01887d180000000000220020c5f4a0a4ea7c0392efe0a9670a73264cffa90b19107cd8a8e9750ff93c77fdfb0400483045022100a9b681f324ff4cf419ab06820d07248cc4e359c77334bf448ae7b5cdf3995ddf022039811f91f55b602368b4ba08a217b82bfd62d1a97dc635deb1457e7cfcc1550b0147304402201ad86a795c3d26881d696fa0a0619c24c4d505718132a82965cc2a609c9d8798022067cd490ce1366cde77e307ced5b13040bbc04991619ea6f49e06cece9a83268b01695221038e81669c085a5846e68e03875113ddb339ecbb7cb11376d4163bca5dc2e2a0c1210348c5c3be9f0e6cf1954ded1c0475beccc4d26aaa9d0cce2dd902538ff1018a112103931140ebe0fbbb7df0be04ed032a54e9589e30339ba7bbb8b0b71b15df1294da53ae00000000')
def test_send_multisig_2(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
nodes = map(lambda index: self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)), range(1, 4))
multisig = proto_types.MultisigRedeemScriptType(
pubkeys=map(lambda n: proto_types.HDNodePathType(node=deserialize(n.xpub), address_n=[2, 1]), nodes),
signatures=[b'', b'', b''],
m=2,
)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("999'/1'/2'/2/1"),
prev_hash=binascii.unhexlify('f41cbedd8becee05a830f418d13aa665125464547db5c7a6cd28f21639fe1228'),
prev_index=0,
script_type=proto_types.SPENDWITNESS,
multisig=multisig,
amount=1605000
)
out1 = proto_types.TxOutputType(
address='T7nY3A3kewpDKumsdhonP4TBDfTXFSc2RNhZxkqmeeszRDHjM5yUn',
amount=1604000,
script_type=proto_types.PAYTOADDRESS
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures1, _) = self.client.sign_tx('Testnet', [inp1], [out1])
# store signature
inp1.multisig.signatures[1] = signatures1[0]
# sign with first key
inp1.address_n[2] = 0x80000001
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures2, serialized_tx) = self.client.sign_tx('Testnet', [inp1], [out1])
# c9348040bbc2024e12dcb4a0b4806b0398646b91acf314da028c3f03dd0179fc
self.assertEqual(binascii.hexlify(serialized_tx), b'010000000001012812fe3916f228cda6c7b57d5464541265a63ad118f430a805eeec8bddbe1cf40000000000ffffffff01a0791800000000002200201e8dda334f11171190b3da72e526d441491464769679a319a2f011da5ad312a10400483045022100cc97f21a7cabc543a9b4ac52424e8f7e420622903f2417a1c08a6af68058ec4a02200baca0b222fc825078d94e8e1b55f174c4828bed16697e4281cda2a0c799eecf01473044022009b8058dc30fa7a13310dd8f1a99c4341c4cd95f771c5a41c4381f956e2344c102205e829c560c0184fd4b4db8971f99711e2a87409afa4df0840b4f12a87b2c8afc0169522102740ec30d0af8591a0dd4a3e3b274e57f3f73bdc0638a9603f9ee6ade0475ba57210311aada919974e882abf0c67b5c0fba00000b26997312ca00345027d22359443021029382591271a79d4b12365fa27c67fad3753150d8eaa987e5a12dc5ba1bb2fa1653ae00000000')
def test_send_multisig_3_change(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
nodes = map(lambda index: self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)), range(1, 4))
multisig = proto_types.MultisigRedeemScriptType(
pubkeys=map(lambda n: proto_types.HDNodePathType(node=deserialize(n.xpub), address_n=[2, 0]), nodes),
signatures=[b'', b'', b''],
m=2,
)
multisig2 = proto_types.MultisigRedeemScriptType(
pubkeys=map(lambda n: proto_types.HDNodePathType(node=deserialize(n.xpub), address_n=[1, 1]), nodes),
signatures=[b'', b'', b''],
m=2,
)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("999'/1'/1'/2/0"),
prev_hash=binascii.unhexlify('c9348040bbc2024e12dcb4a0b4806b0398646b91acf314da028c3f03dd0179fc'),
prev_index=0,
script_type=proto_types.SPENDWITNESS,
multisig=multisig,
amount=1604000
)
out1 = proto_types.TxOutputType(
address_n=self.client.expand_path("999'/1'/1'/1/1"),
amount=1603000,
multisig=multisig2,
script_type=proto_types.PAYTOP2SHWITNESS
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures1, _) = self.client.sign_tx('Testnet', [inp1], [out1])
# store signature
inp1.multisig.signatures[0] = signatures1[0]
# sign with third key
inp1.address_n[2] = 0x80000003
out1.address_n[2] = 0x80000003
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures2, serialized_tx) = self.client.sign_tx('Testnet', [inp1], [out1])
# 31bc1c88ce6ae337a6b3057a16d5bad0b561ad1dfc047d0a7fbb8814668f91e5
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000000101fc7901dd033f8c02da14f3ac916b6498036b80b4a0b4dc124e02c2bb408034c90000000000ffffffff01b87518000000000017a914a8655acf68f785125561158b0f4db9b5d0044047870400473044022057b571986c07f8ccb231811334ad06ee6f87b722495def2e9511c1da46f3433202207b6e95bdd99e7fc7d319486437cb930d40a4af3cd753c4cb960b330badbf7f35014730440220517ecc6d0a2544276921d8fc2077aec4285ab83b1b21f5eb73cdb6187a0583e4022043fb5ab942f8981c04a54c66a57c4d291fad8514d4a8afea09f01f2db7a8f32901695221038e81669c085a5846e68e03875113ddb339ecbb7cb11376d4163bca5dc2e2a0c1210348c5c3be9f0e6cf1954ded1c0475beccc4d26aaa9d0cce2dd902538ff1018a112103931140ebe0fbbb7df0be04ed032a54e9589e30339ba7bbb8b0b71b15df1294da53ae00000000')
def test_send_multisig_4_change(self):
self.setup_mnemonic_allallall()
self.client.set_tx_api(TxApiTestnet)
nodes = map(lambda index: self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)), range(1, 4))
multisig = proto_types.MultisigRedeemScriptType(
pubkeys=map(lambda n: proto_types.HDNodePathType(node=deserialize(n.xpub), address_n=[1, 1]), nodes),
signatures=[b'', b'', b''],
m=2,
)
multisig2 = proto_types.MultisigRedeemScriptType(
pubkeys=map(lambda n: proto_types.HDNodePathType(node=deserialize(n.xpub), address_n=[1, 2]), nodes),
signatures=[b'', b'', b''],
m=2,
)
inp1 = proto_types.TxInputType(
address_n=self.client.expand_path("999'/1'/1'/1/1"),
prev_hash=binascii.unhexlify('31bc1c88ce6ae337a6b3057a16d5bad0b561ad1dfc047d0a7fbb8814668f91e5'),
prev_index=0,
script_type=proto_types.SPENDP2SHWITNESS,
multisig=multisig,
amount=1603000
)
out1 = proto_types.TxOutputType(
address_n=self.client.expand_path("999'/1'/1'/1/2"),
amount=1602000,
multisig=multisig2,
script_type=proto_types.PAYTOWITNESS
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures1, _) = self.client.sign_tx('Testnet', [inp1], [out1])
# store signature
inp1.multisig.signatures[0] = signatures1[0]
# sign with third key
inp1.address_n[2] = 0x80000003
out1.address_n[2] = 0x80000003
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures2, serialized_tx) = self.client.sign_tx('Testnet', [inp1], [out1])
# c0bf56060a109624b4635222696d94a7d533cacea1b3f8245417a4348c045829
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000000101e5918f661488bb7f0a7d04fc1dad61b5d0bad5167a05b3a637e36ace881cbc3100000000232200205b9824093eaf5cdcf8247c00dc0b557a7720957828fcde8384ac11f80a91f403ffffffff01d071180000000000220020e77caf5fbef07b1e461475c02afd4aed877693263d69c81e14617304349b629a040047304402204832553b0da1009da496881e58e8e2e41010cfe5c0161623048093f1b1a817b7022020dad8bf887acf574af80bfe4b39cd24e95019fd5e6b8ae967471e21ddc67354014830450221009e5d60847e7275edcf4619ed8ee462c56a042eef75d17da2d44e6b13d78e50e50220665195492900ef87a5eb8a924fa0ac9afc4fc75ca704ff356dc3a213979970c80169522103f4040006e3561b3e76c6d4113225c84748ab9d55ffd23f9578ab4c18fb0c3b9721020975f2e6922897ff6b80da6412a8d6ebd67e33c9611d081656a53ef967964e5021026b0546f23a6ce6b756c2c30b4176ce6f1c3268744f7aca82668d5116c4f764e453ae00000000')
if __name__ == '__main__':
unittest.main()

View File

@ -1,75 +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>
#
# 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/>.
import unittest
import common
import binascii
import trezorlib.messages_pb2 as proto
import trezorlib.types_pb2 as proto_types
from trezorlib.tx_api import TxApiZcashTestnet
class TestMsgSigntx(common.TrezorTest):
def test_one_one_fee(self):
self.setup_mnemonic_allallall()
# tx: 93373e63cc626c4a7d049ad775d6511bb5eba985f142db660c9b9f955c722f5c
# input 0: 1.234567 TAZ
inp1 = proto_types.TxInputType(
address_n=[2147483692, 2147483649, 2147483648, 0, 0], # tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu
# amount=123456700,
prev_hash=binascii.unhexlify(b'93373e63cc626c4a7d049ad775d6511bb5eba985f142db660c9b9f955c722f5c'),
prev_index=0,
)
out1 = proto_types.TxOutputType(
address='tmJ1xYxP8XNTtCoDgvdmQPSrxh5qZJgy65Z',
amount=123456700 - 1940,
script_type=proto_types.PAYTOADDRESS,
)
with self.client:
self.client.set_tx_api(TxApiZcashTestnet)
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=binascii.unhexlify(b"93373e63cc626c4a7d049ad775d6511bb5eba985f142db660c9b9f955c722f5c"))),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=binascii.unhexlify(b"93373e63cc626c4a7d049ad775d6511bb5eba985f142db660c9b9f955c722f5c"))),
proto.TxRequest(request_type=proto_types.TXEXTRADATA, details=proto_types.TxRequestDetailsType(tx_hash=binascii.unhexlify(b"93373e63cc626c4a7d049ad775d6511bb5eba985f142db660c9b9f955c722f5c"), extra_data_offset=0, extra_data_len=1024)),
proto.TxRequest(request_type=proto_types.TXEXTRADATA, details=proto_types.TxRequestDetailsType(tx_hash=binascii.unhexlify(b"93373e63cc626c4a7d049ad775d6511bb5eba985f142db660c9b9f955c722f5c"), extra_data_offset=1024, extra_data_len=1024)),
proto.TxRequest(request_type=proto_types.TXEXTRADATA, details=proto_types.TxRequestDetailsType(tx_hash=binascii.unhexlify(b"93373e63cc626c4a7d049ad775d6511bb5eba985f142db660c9b9f955c722f5c"), extra_data_offset=2048, extra_data_len=1024)),
proto.TxRequest(request_type=proto_types.TXEXTRADATA, details=proto_types.TxRequestDetailsType(tx_hash=binascii.unhexlify(b"93373e63cc626c4a7d049ad775d6511bb5eba985f142db660c9b9f955c722f5c"), extra_data_offset=3072, extra_data_len=629)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Zcash Testnet', [inp1, ], [out1, ])
# Accepted by network: tx dcc2a10894e0e8a785c2afd4de2d958207329b9acc2b987fd768a09c5efc4547
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000015c2f725c959f9b0c66db42f185a9ebb51b51d675d79a047d4a6c62cc633e3793000000006a4730440220670b2b63d749a7038f9aea6ddf0302fe63bdcad93dafa4a89a1f0e7300ae2484022002c50af43fd867490cea0c527273c5828ff1b9a5115678f155a1830737cf29390121030e669acac1f280d1ddf441cd2ba5e97417bf2689e4bbec86df4f831bf9f7ffd0ffffffff0128c55b07000000001976a9145b157a678a10021243307e4bb58f36375aa80e1088ac00000000')
if __name__ == '__main__':
unittest.main()

View File

@ -1,258 +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>
#
# 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 print_function
import unittest
import common
import binascii
import trezorlib.messages_pb2 as proto
import trezorlib.ckd_public as bip32
import trezorlib.types_pb2 as proto_types
from trezorlib.client import CallException
# Multisig howto:
#
# https://sx.dyne.org/multisig.html
#
class TestMultisig(common.TrezorTest):
def test_2_of_3(self):
self.setup_mnemonic_nopin_nopassphrase()
# key1 = self.client.get_public_node([1])
# key2 = self.client.get_public_node([2])
# key3 = self.client.get_public_node([3])
# xpub:
# print(bip32.serialize(self.client.get_public_node([]).node))
# xpub661MyMwAqRbcF1zGijBb2K6x9YiJPh58xpcCeLvTxMX6spkY3PcpJ4ABcCyWfskq5DDxM3e6Ez5ePCqG5bnPUXR4wL8TZWyoDaUdiWW7bKy
# pubkeys:
# xpub/1: 0338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6
# xpub/2: 038caebd6f753bbbd2bb1f3346a43cd32140648583673a31d62f2dfb56ad0ab9e3
# xpub/3: 03477b9f0f34ae85434ce795f0c5e1e90c9420e5b5fad084d7cce9a487b94a7902
# redeem script:
# 52210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a621038caebd6f753bbbd2bb1f3346a43cd32140648583673a31d62f2dfb56ad0ab9e32103477b9f0f34ae85434ce795f0c5e1e90c9420e5b5fad084d7cce9a487b94a790253ae
# multisig address: 3E7GDtuHqnqPmDgwH59pVC7AvySiSkbibz
# tx: c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52
# input 1: 0.001 BTC
node = bip32.deserialize('xpub661MyMwAqRbcF1zGijBb2K6x9YiJPh58xpcCeLvTxMX6spkY3PcpJ4ABcCyWfskq5DDxM3e6Ez5ePCqG5bnPUXR4wL8TZWyoDaUdiWW7bKy')
multisig = proto_types.MultisigRedeemScriptType(
pubkeys=[
proto_types.HDNodePathType(node=node, address_n=[1]),
proto_types.HDNodePathType(node=node, address_n=[2]),
proto_types.HDNodePathType(node=node, address_n=[3])
],
signatures=[b'', b'', b''],
m=2,
)
# Let's go to sign with key 1
inp1 = proto_types.TxInputType(
address_n=[1],
prev_hash=binascii.unhexlify('c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52'),
prev_index=1,
script_type=proto_types.SPENDMULTISIG,
multisig=multisig,
)
out1 = proto_types.TxOutputType(
address='12iyMbUb4R2K3gre4dHSrbu5azG5KaqVss',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=binascii.unhexlify("c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52"))),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=binascii.unhexlify("c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52"))),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=binascii.unhexlify("c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52"))),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=binascii.unhexlify("c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52"))),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
# Now we have first signature
(signatures1, _) = self.client.sign_tx('Bitcoin', [inp1, ], [out1, ])
self.assertEqual(binascii.hexlify(signatures1[0]), b'3045022100985cc1ba316d140eb4b2d4028d8cd1c451f87bff8ff679858732e516ad04cd3402207af6edda99972af0baa7702a3b7448517c8242e7bca669f6861771cdd16ee058')
# ---------------------------------------
# Let's do second signature using 3rd key
multisig = proto_types.MultisigRedeemScriptType(
pubkeys=[
proto_types.HDNodePathType(node=node, address_n=[1]),
proto_types.HDNodePathType(node=node, address_n=[2]),
proto_types.HDNodePathType(node=node, address_n=[3])
],
signatures=[signatures1[0], b'', b''], # Fill signature from previous signing process
m=2,
)
# Let's do a second signature with key 3
inp3 = proto_types.TxInputType(
address_n=[3],
prev_hash=binascii.unhexlify('c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52'),
prev_index=1,
script_type=proto_types.SPENDMULTISIG,
multisig=multisig,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=binascii.unhexlify("c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52"))),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=binascii.unhexlify("c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52"))),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=binascii.unhexlify("c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52"))),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=binascii.unhexlify("c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52"))),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures2, serialized_tx) = self.client.sign_tx('Bitcoin', [inp3, ], [out1, ])
self.assertEqual(binascii.hexlify(signatures2[0]), b'3045022100f5428fe0531b3095675b40d87cab607ee036fac823b22e8dcec35b65aff6e52b022032129b4577ff923d321a1c70db5a6cec5bcc142cb2c51901af8b989cced23e0d')
# Accepted by network: tx 8382a2b2e3ec8788800c1d46d285dfa9dd4051edddd75982fad166b9273e5ac6
self.assertEqual(binascii.hexlify(serialized_tx), b'010000000152ba4dfcde9c4bed88f55479cdea03e711ae586e9a89352a98230c4cdf1a09c601000000fdfe0000483045022100985cc1ba316d140eb4b2d4028d8cd1c451f87bff8ff679858732e516ad04cd3402207af6edda99972af0baa7702a3b7448517c8242e7bca669f6861771cdd16ee05801483045022100f5428fe0531b3095675b40d87cab607ee036fac823b22e8dcec35b65aff6e52b022032129b4577ff923d321a1c70db5a6cec5bcc142cb2c51901af8b989cced23e0d014c6952210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a621038caebd6f753bbbd2bb1f3346a43cd32140648583673a31d62f2dfb56ad0ab9e32103477b9f0f34ae85434ce795f0c5e1e90c9420e5b5fad084d7cce9a487b94a790253aeffffffff01a0860100000000001976a91412e8391ad256dcdc023365978418d658dfecba1c88ac00000000')
def test_15_of_15(self):
self.setup_mnemonic_nopin_nopassphrase()
"""
pubs = []
for x in range(15):
pubs.append(binascii.hexlify(self.client.get_public_node([x]).node.public_key))
"""
# xpub:
# print(bip32.serialize(self.client.get_public_node([]).node))
# xpub661MyMwAqRbcF1zGijBb2K6x9YiJPh58xpcCeLvTxMX6spkY3PcpJ4ABcCyWfskq5DDxM3e6Ez5ePCqG5bnPUXR4wL8TZWyoDaUdiWW7bKy
node = bip32.deserialize('xpub661MyMwAqRbcF1zGijBb2K6x9YiJPh58xpcCeLvTxMX6spkY3PcpJ4ABcCyWfskq5DDxM3e6Ez5ePCqG5bnPUXR4wL8TZWyoDaUdiWW7bKy')
pubs = []
for x in range(15):
pubs.append(proto_types.HDNodePathType(node=node, address_n=[x]))
# redeeemscript
# 5f21023230848585885f63803a0a8aecdd6538792d5c539215c91698e315bf0253b43d210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a621038caebd6f753bbbd2bb1f3346a43cd32140648583673a31d62f2dfb56ad0ab9e32103477b9f0f34ae85434ce795f0c5e1e90c9420e5b5fad084d7cce9a487b94a79022103fe91eca10602d7dad4c9dab2b2a0858f71e25a219a6940749ce7a48118480dae210234716c01c2dd03fa7ee302705e2b8fbd1311895d94b1dca15e62eedea9b0968f210341fb2ead334952cf60f4481ba435c4693d0be649be01d2cfe9b02018e483e7bd2102dad8b2bce360a705c16e74a50a36459b4f8f4b78f9cd67def29d54ef6f7c7cf9210222dbe3f5f197a34a1d50e2cbe2a1085cac2d605c9e176f9a240e0fd0c669330d2103fb41afab56c9cdb013fda63d777d4938ddc3cb2ad939712da688e3ed333f95982102435f177646bdc717cb3211bf46656ca7e8d642726144778c9ce816b8b8c36ccf2102158d8e20095364031d923c7e9f7f08a14b1be1ddee21fe1a5431168e31345e5521026259794892428ca0818c8fb61d2d459ddfe20e57f50803c7295e6f4e2f5586652102815f910a8689151db627e6e262e0a2075ad5ec2993a6bc1b876a9d420923d681210318f54647f645ff01bd49fedc0219343a6a22d3ea3180a3c3d3097e4b888a8db45fae
# multisig address
# 3QaKF8zobqcqY8aS6nxCD5ZYdiRfL3RCmU
signatures = [b''] * 15
out1 = proto_types.TxOutputType(
address='17kTB7qSk3MupQxWdiv5ZU3zcrZc2Azes1',
amount=10000,
script_type=proto_types.PAYTOADDRESS
)
for x in range(15):
multisig = proto_types.MultisigRedeemScriptType(
pubkeys=pubs,
signatures=signatures,
m=15,
)
inp1 = proto_types.TxInputType(
address_n=[x],
prev_hash=binascii.unhexlify('6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315'),
prev_index=1,
script_type=proto_types.SPENDMULTISIG,
multisig=multisig,
)
with self.client:
(sig, serialized_tx) = self.client.sign_tx('Bitcoin', [inp1, ], [out1, ])
signatures[x] = sig[0]
# Accepted as tx id dd320786d1f58c095be0509dc56b277b6de8f2fb5517f519c6e6708414e3300b
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000011553ef34c683f4d6a8d346844409e6e7fc4ff01eaa25b7e8ce215abbfee3896101000000fd43060048304502210098e23085ad7282de988bf98afa1e9add9c9830009132f8902a9fa4624d5dc98b0220733216e70ab67791aa64be5c83d2050cb4ed9ff7eda2a1acc35da024d2ab2a670147304402201f8c11fb6e90fd616e484986e9451929797eba039882a9abcc203210948060b9022044da031530de7d9747d3c5a8e7cec04b04b7af495c9120b854ce7362af7fa05a01483045022100ea67c70186acef019bdf1551881bf38e6f88186501b64d3a756a2ce18e4ba18002201c35110325653e21e448b60053a4b5dda46b61096faf701a1faca61fcde91f00014730440220315598992f156d2f9d7b4275395fa067aa61ea95829fa17730885c86df4de70d02203eda9ade1656e2198c668603b17e197acb0969ed183ab0841303ea261205618901473044022060fdd6621edde9b8cf6776bc4eef26ace9b57514d725b7214ba11d333520a30e022044c30744f94484aec0f896233c5613a3256878ec0379f566226906b6d1b6061401483045022100b1d907e3574f60f7834c7e9f2e367998ce0461dad7d742d84ef8917d713f41f902203b3ac54f7bb2f7fb8685f582d2a94f7213a37cb508acffe29090cc06ae01588b01483045022100e3bf90ff3ad6395e42f46002f253f94ca0e8ffaa0620f2ceb4fa21493abdca4d02201d4c28b10b740bb2dc4b3695b4205c18f8c0dad2bb69540eb8a36576463cd5280147304402202cfaf9fab7dc1c9f0c3c23bd46bd6d5cea0664d914139fc9add80766ce998808022012db2802c07853e4cbe147afdf0b47e60bdcbcd31f9df19e04c177ed9aa66c6d0147304402207cbc2d83f351eee5ee91df26bb0c7e1cb07fe328cbbcdb0bb9656d37922c497302201b3435d4c71ffd1b34d45892f2a487bd79c8c7f57cc04373287642bb9610cb840147304402202dc3eab30ccb06553703e794212f43ee9a659f5e787a8374e9ea0bf6de0def7402201a70e970c21a807783313ed102bf4f0a3406ac7c84f94bc8194c5e209464d7230147304402206b04530c190c46a879d7771a6ad53acd33547e0d7fd320d5ad0b5b1fdeb5d4c202207b812be81c3419daadc942cca0c55aa32c7759fa7566c6dc35f030ca87a1c5be01483045022100ce523dddd6eef73d5ae7c44c870466e1ac5a7a77d43475e8def024af68977a1e022028be0276435bfa2ea887d6cf89fa829f96c1c7a55edc57bb3fd667d523fd3bf601473044022019410b20ebcd8eb3ee7ec1eff6bf0f9cbfaea82116811c61f3cf24af7e4434b1022009e5823f3349f695be09ae40754185300d8442a22715ddb5ffa17c4213140e7201483045022100964ef26a9074c3cdafffcfbe4bd445933f8c842ba11fd887922adcf7fabe0c82022023055d94c75ab223c767fbaa825c917e9beecbc7d5758cccf20d886c63d4b72a0147304402207aa3a98197697d258a8baae681f0b4c0ee682982f4205534e6c95a37dabaddd60220517a7ed5c03da2f242e17ccfdae0d81d6f454d7f9ea931fc62df6c0eab922186014d01025f21023230848585885f63803a0a8aecdd6538792d5c539215c91698e315bf0253b43d210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a621038caebd6f753bbbd2bb1f3346a43cd32140648583673a31d62f2dfb56ad0ab9e32103477b9f0f34ae85434ce795f0c5e1e90c9420e5b5fad084d7cce9a487b94a79022103fe91eca10602d7dad4c9dab2b2a0858f71e25a219a6940749ce7a48118480dae210234716c01c2dd03fa7ee302705e2b8fbd1311895d94b1dca15e62eedea9b0968f210341fb2ead334952cf60f4481ba435c4693d0be649be01d2cfe9b02018e483e7bd2102dad8b2bce360a705c16e74a50a36459b4f8f4b78f9cd67def29d54ef6f7c7cf9210222dbe3f5f197a34a1d50e2cbe2a1085cac2d605c9e176f9a240e0fd0c669330d2103fb41afab56c9cdb013fda63d777d4938ddc3cb2ad939712da688e3ed333f95982102435f177646bdc717cb3211bf46656ca7e8d642726144778c9ce816b8b8c36ccf2102158d8e20095364031d923c7e9f7f08a14b1be1ddee21fe1a5431168e31345e5521026259794892428ca0818c8fb61d2d459ddfe20e57f50803c7295e6f4e2f5586652102815f910a8689151db627e6e262e0a2075ad5ec2993a6bc1b876a9d420923d681210318f54647f645ff01bd49fedc0219343a6a22d3ea3180a3c3d3097e4b888a8db45faeffffffff0110270000000000001976a9144a087d89f8ad16ca029c675b037c02fd1c5f9aec88ac00000000')
def test_missing_pubkey(self):
self.setup_mnemonic_nopin_nopassphrase()
# key1 = self.client.get_public_node([1])
# key2 = self.client.get_public_node([2])
# key3 = self.client.get_public_node([3])
# pubkeys:
# 0338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6
# 038caebd6f753bbbd2bb1f3346a43cd32140648583673a31d62f2dfb56ad0ab9e3
# 03477b9f0f34ae85434ce795f0c5e1e90c9420e5b5fad084d7cce9a487b94a7902
# multisig address: 3E7GDtuHqnqPmDgwH59pVC7AvySiSkbibz
# xpub:
# print(bip32.serialize(self.client.get_public_node([]).node))
# xpub661MyMwAqRbcF1zGijBb2K6x9YiJPh58xpcCeLvTxMX6spkY3PcpJ4ABcCyWfskq5DDxM3e6Ez5ePCqG5bnPUXR4wL8TZWyoDaUdiWW7bKy
node = bip32.deserialize('xpub661MyMwAqRbcF1zGijBb2K6x9YiJPh58xpcCeLvTxMX6spkY3PcpJ4ABcCyWfskq5DDxM3e6Ez5ePCqG5bnPUXR4wL8TZWyoDaUdiWW7bKy')
multisig = proto_types.MultisigRedeemScriptType(
pubkeys=[
proto_types.HDNodePathType(node=node, address_n=[1]),
proto_types.HDNodePathType(node=node, address_n=[2]),
proto_types.HDNodePathType(node=node, address_n=[3])
],
signatures=[b'', b'', b''],
m=2,
)
# Let's go to sign with key 10, which is NOT in pubkeys
inp1 = proto_types.TxInputType(
address_n=[10],
prev_hash=binascii.unhexlify('c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52'),
prev_index=1,
script_type=proto_types.SPENDMULTISIG,
multisig=multisig,
)
out1 = proto_types.TxOutputType(
address='12iyMbUb4R2K3gre4dHSrbu5azG5KaqVss',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
with self.client:
# It should throw Failure 'Pubkey not found in multisig script'
self.assertRaises(CallException, self.client.sign_tx, 'Bitcoin', [inp1, ], [out1, ])
if __name__ == '__main__':
unittest.main()

View File

@ -1,381 +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>
#
# 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/>.
import unittest
import common
import binascii
import trezorlib.messages_pb2 as proto
import trezorlib.ckd_public as bip32
import trezorlib.types_pb2 as proto_types
class TestMultisigChange(common.TrezorTest):
node_ext1 = bip32.deserialize('xpub6E2LkiC2h7icfcjXPFDmwufDRaaTjTRYcS2nD7eGQeFx1WwZxxvCya5GefiJND6ddJnAjzzMusLcCnu6WyhZPrF6e5G71MWjNJVfs6u9csg')
# m/1 => 02c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e
# m/2 => 0375b9dfaad928ce1a7eed88df7c084e67d99e9ab74332419458a9a45779706801
node_ext2 = bip32.deserialize('xpub6FKKCwdfD85eHmKn7d3mmbhqsHnGkB2n7Hmre29gbnR1cR4U1wrtf2akh1VVqjcTdfkxmwPjQyYPhLLgwBijfFPAYqTZzcjj4awB1BMUxq2')
# m/1 => 0388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c1
# m/2 => 03a04f945d5a3685729dde697d574076de4bdf38e904f813b22a851548e1110fc0
node_ext3 = bip32.deserialize('xpub69rsKg2fef3GzETrukhsR3U9i4nL3dXKy3cjSpm44Cg8waqrnyupanaLQt4bYjn2HTmH1NusFt9DAh6absMQqE4E66q7EYTyEsorZKXdWWx')
# m/1 => 02e0c21e2a7cf00b94c5421725acff97f9826598b91f2340c5ddda730caca7d648
# m/2 => 03928301ffb8c0d7a364b794914c716ba3107cc78a6fe581028b0d8638b22e8573
node_int = bip32.deserialize('xpub661MyMwAqRbcF1zGijBb2K6x9YiJPh58xpcCeLvTxMX6spkY3PcpJ4ABcCyWfskq5DDxM3e6Ez5ePCqG5bnPUXR4wL8TZWyoDaUdiWW7bKy')
# m/1 => 0338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6
# m/2 => 038caebd6f753bbbd2bb1f3346a43cd32140648583673a31d62f2dfb56ad0ab9e3
# ext1 + ext2 + int
# redeemscript (2 of 3): 522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c1210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a653ae
# multisig address: 3Gj7y1FdTppx2JEDqYqAEZFnKCA4GRysKF
# tx: d1d08ea63255af4ad16b098e9885a252632086fa6be53301521d05253ce8a73d
# input 0: 0.001 BTC
# ext1 + int + ext2
# redeemscript (2 of 3): 522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c153ae
# multisig address: 3QsvfB6d1LzYcpm8xyhS1N1HBRrzHTgLHB
# tx: a6e2829d089cee47e481b1a753a53081b40738cc87e38f1d9b23ab57d9ad4396
# input 0: 0.001 BTC
# ext1 + ext3 + int
# redeemscript (2 of 3): 522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e2102e0c21e2a7cf00b94c5421725acff97f9826598b91f2340c5ddda730caca7d648210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a653ae
# multisig address: 37LvC1Q5CyKbMbKMncEJdXxqGhHxrBEgPE
# tx: e4bc1ae5e5007a08f2b3926fe11c66612e8f73c6b00c69c7027213b84d259be3
# input 1: 0.001 BTC
multisig_in1 = proto_types.MultisigRedeemScriptType(
pubkeys=[
proto_types.HDNodePathType(node=node_ext1, address_n=[1]),
proto_types.HDNodePathType(node=node_ext2, address_n=[1]),
proto_types.HDNodePathType(node=node_int, address_n=[1])
],
signatures=[b'', b'', b''],
m=2,
)
multisig_in2 = proto_types.MultisigRedeemScriptType(
pubkeys=[
proto_types.HDNodePathType(node=node_ext1, address_n=[1]),
proto_types.HDNodePathType(node=node_int, address_n=[1]),
proto_types.HDNodePathType(node=node_ext2, address_n=[1])
],
signatures=[b'', b'', b''],
m=2,
)
multisig_in3 = proto_types.MultisigRedeemScriptType(
pubkeys=[
proto_types.HDNodePathType(node=node_ext1, address_n=[1]),
proto_types.HDNodePathType(node=node_ext3, address_n=[1]),
proto_types.HDNodePathType(node=node_int, address_n=[1])
],
signatures=[b'', b'', b''],
m=2,
)
inp1 = proto_types.TxInputType(
address_n=[1],
prev_hash=binascii.unhexlify('d1d08ea63255af4ad16b098e9885a252632086fa6be53301521d05253ce8a73d'),
prev_index=0,
script_type=proto_types.SPENDMULTISIG,
multisig=multisig_in1,
)
inp2 = proto_types.TxInputType(
address_n=[1],
prev_hash=binascii.unhexlify('a6e2829d089cee47e481b1a753a53081b40738cc87e38f1d9b23ab57d9ad4396'),
prev_index=0,
script_type=proto_types.SPENDMULTISIG,
multisig=multisig_in2,
)
inp3 = proto_types.TxInputType(
address_n=[1],
prev_hash=binascii.unhexlify('e4bc1ae5e5007a08f2b3926fe11c66612e8f73c6b00c69c7027213b84d259be3'),
prev_index=1,
script_type=proto_types.SPENDMULTISIG,
multisig=multisig_in3,
)
def _responses(self, inp1, inp2, change=0):
resp = [
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=inp1.prev_hash)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=inp1.prev_hash)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=inp1.prev_hash)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=inp1.prev_hash)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=inp2.prev_hash)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=inp2.prev_hash)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=inp2.prev_hash)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=inp2.prev_hash)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
]
if change != 1:
resp.append(
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput)
)
resp.append(
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1))
)
if change != 2:
resp.append(
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput)
)
resp += [
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
]
return resp
# both outputs are external
def test_external_external(self):
self.setup_mnemonic_nopin_nopassphrase()
out1 = proto_types.TxOutputType(
address='12iyMbUb4R2K3gre4dHSrbu5azG5KaqVss',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
out2 = proto_types.TxOutputType(
address='17kTB7qSk3MupQxWdiv5ZU3zcrZc2Azes1',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
with self.client:
self.client.set_expected_responses(self._responses(self.inp1, self.inp2))
(_, serialized_tx) = self.client.sign_tx('Bitcoin', [self.inp1, self.inp2, ], [out1, out2, ])
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000023da7e83c25051d520133e56bfa86206352a285988e096bd14aaf5532a68ed0d100000000b500483045022100c4116c9a584083021dacb690d4d4938027cc3f2085dc15157162b589f2a0b52f02200bdec59f76376255afc7b76f759106f6f00edf0134db2a0ae5d28000ea517fd2014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c1210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a653aeffffffff9643add957ab239b1d8fe387cc3807b48130a553a7b181e447ee9c089d82e2a600000000b400473044022044e77c67a5a78c8eb4f304cf23972a7763cce6f7dc3587d6e77e2aa05212ea6402200be874d39c32ad2475d03342cb0b164ec618297231c519186e3d0efde7a3374d014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c153aeffffffff02a0860100000000001976a91412e8391ad256dcdc023365978418d658dfecba1c88aca0860100000000001976a9144a087d89f8ad16ca029c675b037c02fd1c5f9aec88ac00000000')
# first external, second internal
def test_external_internal(self):
self.setup_mnemonic_nopin_nopassphrase()
out1 = proto_types.TxOutputType(
address='12iyMbUb4R2K3gre4dHSrbu5azG5KaqVss',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
out2 = proto_types.TxOutputType(
address_n=[4],
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
with self.client:
self.client.set_expected_responses(self._responses(self.inp1, self.inp2, change=2))
(_, serialized_tx) = self.client.sign_tx('Bitcoin', [self.inp1, self.inp2, ], [out1, out2, ])
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000023da7e83c25051d520133e56bfa86206352a285988e096bd14aaf5532a68ed0d100000000b5004830450221008f48ee3c6e69f8d2aeea9c482e3e80233fc83d78eb1ac7416362b25ae57d3eee02207f6b568f8f611efb17bd6bf8d0b32d334aa4110a2cc97a06f48aba4d045b7cd4014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c1210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a653aeffffffff9643add957ab239b1d8fe387cc3807b48130a553a7b181e447ee9c089d82e2a600000000b40047304402206c5f93cbedc06ac1bae846d850a27c56b0e6f75ef247d3d67a10bbe8ea9da90302203d64f4803c0cbe5703268d58a80d54a3ad72cb1b856f19a6c6c999aad011a5b9014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c153aeffffffff02a0860100000000001976a91412e8391ad256dcdc023365978418d658dfecba1c88aca0860100000000001976a914f0a2b64e56ee2ff57126232f84af6e3a41d4055088ac00000000')
# first internal, second external
def test_internal_external(self):
self.setup_mnemonic_nopin_nopassphrase()
out1 = proto_types.TxOutputType(
address_n=[4],
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
out2 = proto_types.TxOutputType(
address='17kTB7qSk3MupQxWdiv5ZU3zcrZc2Azes1',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
with self.client:
self.client.set_expected_responses(self._responses(self.inp1, self.inp2, change=1))
(_, serialized_tx) = self.client.sign_tx('Bitcoin', [self.inp1, self.inp2, ], [out1, out2, ])
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000023da7e83c25051d520133e56bfa86206352a285988e096bd14aaf5532a68ed0d100000000b4004730440220740f305af9cd10f290b0d5dd27968d3c08f313d58e70feb260e076bd57d427bd02202c0296b38e82993983b971196589a2c74cdc4931a2da88aa2c2bd89e58a3fdb2014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c1210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a653aeffffffff9643add957ab239b1d8fe387cc3807b48130a553a7b181e447ee9c089d82e2a600000000b400473044022042f53a8cd53762fb95113d11f56f050dab9dead9a2026807c728d5c42ed62e9902202e708162a50ca16f5fac082c1a2a5350fcb74cbfce39968e76300a36457f45a7014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c153aeffffffff02a0860100000000001976a914f0a2b64e56ee2ff57126232f84af6e3a41d4055088aca0860100000000001976a9144a087d89f8ad16ca029c675b037c02fd1c5f9aec88ac00000000')
# both outputs are external
def test_multisig_external_external(self):
self.setup_mnemonic_nopin_nopassphrase()
out1 = proto_types.TxOutputType(
address='3796Q9jVirg2KY1WQRqtmHKXCoSk8MB7Td',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
out2 = proto_types.TxOutputType(
address='3CTPCg3ksh59jWt9zQpTPHCSQDCdJoQQ9d',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
with self.client:
self.client.set_expected_responses(self._responses(self.inp1, self.inp2))
(_, serialized_tx) = self.client.sign_tx('Bitcoin', [self.inp1, self.inp2, ], [out1, out2, ])
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000023da7e83c25051d520133e56bfa86206352a285988e096bd14aaf5532a68ed0d100000000b500483045022100915e3761efb41895d40fa3bf8d3a68be7eb949e2411ec5655e231bbb334925ea02205814166b786a912f8f47315c9ede4955d2dfc70bb0b51230fccaaacf5a39a0ae014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c1210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a653aeffffffff9643add957ab239b1d8fe387cc3807b48130a553a7b181e447ee9c089d82e2a600000000b400473044022018ca5516ee127eeeb8c70f10c267dd803b599688eade659e3b210bbf1712fffe02206c1adb35e672e67ee102dc232456ac5edc86f58f83d698995981e68d2a2a4294014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c153aeffffffff02a08601000000000017a9143bc72e27ec21644ace15b367ef7ba491f2507eb587a08601000000000017a9147615527d78854293edadf83682ea26937f8a51bb8700000000')
# inputs match, change matches (first is change)
def test_multisig_change_match_first(self):
self.setup_mnemonic_nopin_nopassphrase()
multisig_out1 = proto_types.MultisigRedeemScriptType(
pubkeys=[
proto_types.HDNodePathType(node=self.node_int, address_n=[1]),
proto_types.HDNodePathType(node=self.node_ext1, address_n=[1]),
proto_types.HDNodePathType(node=self.node_ext2, address_n=[1])
],
signatures=[b'', b'', b''],
m=2,
)
out1 = proto_types.TxOutputType(
address_n=[1],
multisig=multisig_out1,
amount=100000,
script_type=proto_types.PAYTOMULTISIG
)
out2 = proto_types.TxOutputType(
address='3CTPCg3ksh59jWt9zQpTPHCSQDCdJoQQ9d',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
with self.client:
self.client.set_expected_responses(self._responses(self.inp1, self.inp2, change=1))
(_, serialized_tx) = self.client.sign_tx('Bitcoin', [self.inp1, self.inp2, ], [out1, out2, ])
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000023da7e83c25051d520133e56bfa86206352a285988e096bd14aaf5532a68ed0d100000000b40047304402203cb26eac850f590951b12b513a5369c0b301c6d3ae1cd251aa837ce35427bdec0220289834c8c5cb837351ae06498d77fa6707611c09d628864a1f0a7e1d381bddd8014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c1210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a653aeffffffff9643add957ab239b1d8fe387cc3807b48130a553a7b181e447ee9c089d82e2a600000000b40047304402207c2e39254d1e9cff42b523bcc0bf5ab66ae0c584deb2413759d9b269b1fe9e6f02205bc93a1884625b2359247c15a57e4e80b184b21a5f95e7f5ce846323236e30ac014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c153aeffffffff02a08601000000000017a914b69a5c6a63c01a09a90eb690031963f737cf96ed87a08601000000000017a9147615527d78854293edadf83682ea26937f8a51bb8700000000')
# inputs match, change matches (second is change)
def test_multisig_change_match_second(self):
self.setup_mnemonic_nopin_nopassphrase()
multisig_out2 = proto_types.MultisigRedeemScriptType(
pubkeys=[
proto_types.HDNodePathType(node=self.node_int, address_n=[2]),
proto_types.HDNodePathType(node=self.node_ext1, address_n=[2]),
proto_types.HDNodePathType(node=self.node_ext2, address_n=[2])
],
signatures=[b'', b'', b''],
m=2,
)
out1 = proto_types.TxOutputType(
address='37Wf955dcCaFSJmiNaGpacczMwj7iC8JMx',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
out2 = proto_types.TxOutputType(
address_n=[2],
multisig=multisig_out2,
amount=100000,
script_type=proto_types.PAYTOMULTISIG
)
with self.client:
self.client.set_expected_responses(self._responses(self.inp1, self.inp2, change=2))
(_, serialized_tx) = self.client.sign_tx('Bitcoin', [self.inp1, self.inp2, ], [out1, out2, ])
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000023da7e83c25051d520133e56bfa86206352a285988e096bd14aaf5532a68ed0d100000000b5004830450221008d5710ba7df3c32358a723c69458acc81a296646cad262217975ba00b24fdc6402201623a3e3778e6abad9025343cef6fad361a054463f928509324ee862a2e84e6a014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c1210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a653aeffffffff9643add957ab239b1d8fe387cc3807b48130a553a7b181e447ee9c089d82e2a600000000b400473044022014d07e6a67c14a81d1042be2990d4c4ac29d9a46ba051168a9ccc09e987d97e202203cfe6714cff04421a90d5a4507f875515a1357fc2df306a44617ae7f88c7fcd1014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c153aeffffffff02a08601000000000017a9143fdb3ed6e85c87d77f263be3b0d0abc508fe4e3787a08601000000000017a914021809d0cb4a6fcf436e6b8cc743511b09d183218700000000')
# inputs match, change mismatches (second is change)
def test_multisig_mismatch_change(self):
self.setup_mnemonic_nopin_nopassphrase()
multisig_out2 = proto_types.MultisigRedeemScriptType(
pubkeys=[
proto_types.HDNodePathType(node=self.node_int, address_n=[2]),
proto_types.HDNodePathType(node=self.node_ext1, address_n=[2]),
proto_types.HDNodePathType(node=self.node_ext3, address_n=[2])
],
signatures=[b'', b'', b''],
m=2,
)
out1 = proto_types.TxOutputType(
address='3796Q9jVirg2KY1WQRqtmHKXCoSk8MB7Td',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
out2 = proto_types.TxOutputType(
address_n=[2],
multisig=multisig_out2,
amount=100000,
script_type=proto_types.PAYTOMULTISIG
)
with self.client:
self.client.set_expected_responses(self._responses(self.inp1, self.inp2))
(_, serialized_tx) = self.client.sign_tx('Bitcoin', [self.inp1, self.inp2, ], [out1, out2, ])
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000023da7e83c25051d520133e56bfa86206352a285988e096bd14aaf5532a68ed0d100000000b40047304402202a6238e8c9955a3d01609cbdaafcf47b0a53b2eabe2e28cf942fe9e253457eba02207f67afb4c35a8d28603e71a0696d0c123c0ca2370d78076c692ca3036c0a2c35014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c1210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a653aeffffffff9643add957ab239b1d8fe387cc3807b48130a553a7b181e447ee9c089d82e2a600000000b40047304402200e87ee683b27f3995a2f8c9e9b4b17e24399d43a4c69ce5402105b6b93ac63cf0220201ba91db1f4ca2768f9277c115e95c2297bbe40969dcf9d10d0836a75c8ac9c014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a6210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c153aeffffffff02a08601000000000017a9143bc72e27ec21644ace15b367ef7ba491f2507eb587a08601000000000017a9143f22da0a6d4a4341be319e48e7b51b5a113fda208700000000')
# inputs mismatch, change matches with first input
def test_multisig_mismatch_inputs(self):
self.setup_mnemonic_nopin_nopassphrase()
multisig_out1 = proto_types.MultisigRedeemScriptType(
pubkeys=[
proto_types.HDNodePathType(node=self.node_ext1, address_n=[1]),
proto_types.HDNodePathType(node=self.node_ext2, address_n=[1]),
proto_types.HDNodePathType(node=self.node_int, address_n=[1])
],
signatures=[b'', b'', b''],
m=2,
)
out1 = proto_types.TxOutputType(
address_n=[1],
multisig=multisig_out1,
amount=100000,
script_type=proto_types.PAYTOMULTISIG
)
out2 = proto_types.TxOutputType(
address='3CTPCg3ksh59jWt9zQpTPHCSQDCdJoQQ9d',
amount=100000,
script_type=proto_types.PAYTOADDRESS
)
with self.client:
self.client.set_expected_responses(self._responses(self.inp1, self.inp3))
(_, serialized_tx) = self.client.sign_tx('Bitcoin', [self.inp1, self.inp3, ], [out1, out2, ])
self.assertEqual(binascii.hexlify(serialized_tx), b'01000000023da7e83c25051d520133e56bfa86206352a285988e096bd14aaf5532a68ed0d100000000b40047304402204b7d6c7e9feef91209cbdf4deaf855696dc22a40e57bd3eafd5e00b0ee41d9de0220262c5a05d0b46ef98fddfef3831b3ebb6841ffbeb10666f8fb6f8d2e3023e30d014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e210388460dc439f4c8f5bcfc268c36e11b4375cad5c3535c336cfdf8c32c3afad5c1210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a653aeffffffffe39b254db8137202c7690cb0c6738f2e61661ce16f92b3f2087a00e5e51abce401000000b500483045022100bb2118da21c8a84f115b655f640f269a40be77ae2c0af9c5ffd8260a85dbfc7702202e7b5b6c05b8f50bd879dbee88828e80e85448d686b63a1a50e99d921923f6f5014c69522102c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e2102e0c21e2a7cf00b94c5421725acff97f9826598b91f2340c5ddda730caca7d648210338d78612e990f2eea0c426b5e48a8db70b9d7ed66282b3b26511e0b1c75515a653aeffffffff02a08601000000000017a914a4efc33d43d7a8a0040182c76ab624ff862f50d287a08601000000000017a9147615527d78854293edadf83682ea26937f8a51bb8700000000')
if __name__ == '__main__':
unittest.main()

View File

@ -1,119 +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>
#
# 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/>.
import unittest
import binascii
import common
import trezorlib.messages_pb2 as proto
import trezorlib.types_pb2 as proto_types
from trezorlib.client import CallException
TXHASH_d5f65e = binascii.unhexlify(b'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')
class TestOpReturn(common.TrezorTest):
def test_opreturn(self):
self.setup_mnemonic_nopin_nopassphrase()
# tx: d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882
# input 0: 0.0039 BTC
inp1 = proto_types.TxInputType(
address_n=[0], # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e
# amount=390000,
prev_hash=TXHASH_d5f65e,
prev_index=0,
)
out1 = proto_types.TxOutputType(
address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
amount=390000 - 10000,
script_type=proto_types.PAYTOADDRESS,
)
out2 = proto_types.TxOutputType(
op_return_data=b'test of the op_return data',
amount=0,
script_type=proto_types.PAYTOOPRETURN,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=binascii.unhexlify("d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882"))),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=binascii.unhexlify("d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882"))),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=binascii.unhexlify("d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882"))),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=binascii.unhexlify("d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882"))),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.ButtonRequest(code=proto_types.ButtonRequest_ConfirmOutput),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.ButtonRequest(code=proto_types.ButtonRequest_SignTx),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=1)),
proto.TxRequest(request_type=proto_types.TXFINISHED),
])
(signatures, serialized_tx) = self.client.sign_tx('Bitcoin', [inp1, ], [out1, out2])
self.assertEqual(binascii.hexlify(serialized_tx), b'010000000182488650ef25a58fef6788bd71b8212038d7f2bbe4750bc7bcb44701e85ef6d5000000006a4730440220187b7b9c340a32fc8445418ad11fb3827d2e8bac7d730e1c9ad800353e7ba62f02206c0c5820ba8882c82923a39aee8d36d6d32e13daed73f7a3d6199de5f8e7ddfd0121023230848585885f63803a0a8aecdd6538792d5c539215c91698e315bf0253b43dffffffff0260cc0500000000001976a914de9b2a8da088824e8fe51debea566617d851537888ac00000000000000001c6a1a74657374206f6620746865206f705f72657475726e206461746100000000')
def test_nonzero_opreturn(self):
self.setup_mnemonic_nopin_nopassphrase()
# tx: d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882
# input 0: 0.0039 BTC
inp1 = proto_types.TxInputType(
address_n=[0], # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e
# amount=390000,
prev_hash=TXHASH_d5f65e,
prev_index=0,
)
out1 = proto_types.TxOutputType(
address='1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
amount=390000 - 10000 - 10000,
script_type=proto_types.PAYTOADDRESS,
)
out1 = proto_types.TxOutputType(
op_return_data=b'test of the op_return data',
amount=10000,
script_type=proto_types.PAYTOOPRETURN,
)
with self.client:
self.client.set_expected_responses([
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.TxRequest(request_type=proto_types.TXMETA, details=proto_types.TxRequestDetailsType(tx_hash=binascii.unhexlify("d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882"))),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=binascii.unhexlify("d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882"))),
proto.TxRequest(request_type=proto_types.TXINPUT, details=proto_types.TxRequestDetailsType(request_index=1, tx_hash=binascii.unhexlify("d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882"))),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0, tx_hash=binascii.unhexlify("d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882"))),
proto.TxRequest(request_type=proto_types.TXOUTPUT, details=proto_types.TxRequestDetailsType(request_index=0)),
proto.Failure()
])
self.assertRaises(CallException, self.client.sign_tx, 'Bitcoin', [inp1, ], [out1, ])
if __name__ == '__main__':
unittest.main()

View File

@ -1,39 +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>
#
# 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/>.
import common
import unittest
from trezorlib import ckd_public
class TestCkdPublic(unittest.TestCase):
def test_ckd(self):
xpub1 = 'xpub661MyMwAqRbcEnKbXcCqD2GT1di5zQxVqoHPAgHNe8dv5JP8gWmDproS6kFHJnLZd23tWevhdn4urGJ6b264DfTGKr8zjmYDjyDTi9U7iyT'
node1 = ckd_public.deserialize(xpub1)
node2 = ckd_public.public_ckd(node1, [0])
node3 = ckd_public.public_ckd(node1, [0, 0])
xpub2 = ckd_public.serialize(node2)
xpub3 = ckd_public.serialize(node3)
self.assertEqual(xpub2, 'xpub67ymn1YTdE2iSGXitxUEZeUdHF2FsejJATroeAxVMtzTAK9o3vjmFLrE7TqE1X76iobkVc3p8h3gNzNRTwPeQGYW3CCmYCG8n5ThVkXaQzs')
self.assertEqual(xpub3, 'xpub6BD2MwdEg5PJPqiGetL9DJs7oDo6zP3XwAABX2vAQb5eLpY3QhHGUEm25V4nkQhnFMsqEVfTwtax2gKz8EFrt1PnBN6xQjE9jGmWDR6modu')
if __name__ == '__main__':
unittest.main()

View File

@ -1,45 +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>
#
# 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/>.
import common
import unittest
from trezorlib.tx_api import TxApiBitcoin, TxApiTestnet
class TestTxApi(unittest.TestCase):
def test_get(self):
TxApiBitcoin.get_tx('39a29e954977662ab3879c66fb251ef753e0912223a83d1dcb009111d28265e5')
TxApiBitcoin.get_tx('54aa5680dea781f45ebb536e53dffc526d68c0eb5c00547e323b2c32382dfba3')
TxApiBitcoin.get_tx('58497a7757224d1ff1941488d23087071103e5bf855f4c1c44e5c8d9d82ca46e')
TxApiBitcoin.get_tx('6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315')
TxApiBitcoin.get_tx('a6e2829d089cee47e481b1a753a53081b40738cc87e38f1d9b23ab57d9ad4396')
TxApiBitcoin.get_tx('c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52')
TxApiBitcoin.get_tx('c63e24ed820c5851b60c54613fbc4bcb37df6cd49b4c96143e99580a472f79fb')
TxApiBitcoin.get_tx('c6be22d34946593bcad1d2b013e12f74159e69574ffea21581dad115572e031c')
TxApiBitcoin.get_tx('d1d08ea63255af4ad16b098e9885a252632086fa6be53301521d05253ce8a73d')
TxApiBitcoin.get_tx('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')
TxApiBitcoin.get_tx('e4bc1ae5e5007a08f2b3926fe11c66612e8f73c6b00c69c7027213b84d259be3')
TxApiTestnet.get_tx('6f90f3c7cbec2258b0971056ef3fe34128dbde30daa9c0639a898f9977299d54')
TxApiTestnet.get_tx('d6da21677d7cca5f42fbc7631d062c9ae918a0254f7c6c22de8e8cb7fd5b8236')
if __name__ == '__main__':
unittest.main()

32
tools/build_protobuf Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
CURDIR=$(pwd)
PB2DIR=$CURDIR/pb2
OUTDIR=../trezorlib/messages
INDEX=$OUTDIR/__init__.py
rm -f $OUTDIR/[A-Z]*.py
mkdir -p $OUTDIR
mkdir -p $PB2DIR
touch $PB2DIR/__init__.py
rm -f $INDEX
echo '# Automatically generated by pb2py' >> $INDEX
echo 'from __future__ import absolute_import' >> $INDEX
echo '' >> $INDEX
for i in types messages ; do
# Compile .proto files to python2 modules using google protobuf library
cd $CURDIR/../../trezor-common/protob
protoc --python_out=$PB2DIR -I/usr/include -I. $i.proto
done
# hack to make output python 3 compatible
sed -i 's/^import types_pb2/from . import types_pb2/g' $CURDIR/pb2/messages_pb2.py
for i in types messages ; do
# Convert google protobuf library to trezor's internal format
cd $CURDIR
./pb2py -p $CURDIR -l $INDEX $i $OUTDIR
done
rm -rf $PB2DIR

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
from __future__ import print_function
'''
@ -16,52 +16,44 @@ import hashlib
import binascii
from trezorlib.client import TrezorClient
from trezorlib.transport_hid import HidTransport
# Python2 vs Python3
try:
input = raw_input
except NameError:
pass
from trezorlib.transport import enumerate_devices
def wait_for_devices():
devices = HidTransport.enumerate()
devices = enumerate_devices()
while not len(devices):
sys.stderr.write("Please connect TREZOR to computer and press Enter...")
input()
devices = HidTransport.enumerate()
devices = enumerate_devices()
return devices
def choose_device(devices):
if not len(devices):
raise Exception("No TREZOR connected!")
raise RuntimeError("No TREZOR connected!")
if len(devices) == 1:
try:
return HidTransport(devices[0])
return devices[0]
except IOError:
raise Exception("Device is currently in use")
raise RuntimeError("Device is currently in use")
i = 0
sys.stderr.write("----------------------------\n")
sys.stderr.write("Available devices:\n")
for d in devices:
try:
t = HidTransport(d)
client = TrezorClient(d)
except IOError:
sys.stderr.write("[-] <device is currently in use>\n")
continue
client = TrezorClient(t)
if client.features.label:
sys.stderr.write("[%d] %s\n" % (i, client.features.label))
else:
sys.stderr.write("[%d] <no label>\n" % i)
t.close()
client.close()
i += 1
sys.stderr.write("----------------------------\n")
@ -69,9 +61,9 @@ def choose_device(devices):
try:
device_id = int(input())
return HidTransport(devices[device_id])
return devices[device_id]
except:
raise Exception("Invalid choice, exiting...")
raise ValueError("Invalid choice, exiting...")
def main():
@ -103,7 +95,7 @@ def main():
passw = hashlib.sha256(trezor_entropy + urandom_entropy).digest()
if len(passw) != 32:
raise Exception("32 bytes password expected")
raise ValueError("32 bytes password expected")
bip32_path = [10, 0]
passw_encrypted = client.encrypt_keyvalue(bip32_path, label, passw, False, True)

View File

@ -1,21 +1,11 @@
#!/usr/bin/env python
from __future__ import print_function
#!/usr/bin/env python3
from trezorlib.client import TrezorClient
from trezorlib.transport_hid import HidTransport
from trezorlib.transport import get_transport
def main():
# List all connected TREZORs on USB
devices = HidTransport.enumerate()
# Check whether we found any
if len(devices) == 0:
print('No TREZOR found')
return
# Use first connected device
transport = HidTransport(devices[0])
transport = get_transport()
# Creates object for manipulating TREZOR
client = TrezorClient(transport)

48
tools/mem_flashblock.py Executable file
View File

@ -0,0 +1,48 @@
#!/usr/bin/env python3
from trezorlib.debuglink import DebugLink
from trezorlib.client import TrezorClient
from trezorlib.transport import enumerate_devices
import binascii
import sys
sectoraddrs = [0x8000000, 0x8004000, 0x8008000, 0x800c000,
0x8010000, 0x8020000, 0x8040000, 0x8060000,
0x8080000, 0x80a0000, 0x80c0000, 0x80f0000]
sectorlens = [0x4000, 0x4000, 0x4000, 0x4000,
0x8000, 0x10000, 0x10000, 0x10000,
0x10000, 0x10000, 0x10000, 0x10000]
def main():
# 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:
print('No TREZOR found')
return
# Use first connected device
transport = devices[0]
debug_transport = devices[0].find_debug()
# Creates object for manipulating TREZOR
client = TrezorClient(transport)
debug = DebugLink(debug_transport)
sector = int(sys.argv[1])
f = open(sys.argv[2], "rb")
content = f.read(sectorlens[sector])
if (len(content) != sectorlens[sector]):
print("Not enough bytes in file")
return
debug.flash_erase(sector)
step = 0x400
for offset in range(0, sectorlens[sector], step):
debug.memory_write(sectoraddrs[sector] + offset, content[offset:offset + step], flash=True)
client.close()
if __name__ == '__main__':
main()

View File

@ -1,9 +1,7 @@
#!/usr/bin/env python
from __future__ import print_function
#!/usr/bin/env python3
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:
@ -25,10 +23,10 @@ def main():
return
# Use first connected device
transport = HidTransport(devices[0])
transport = devices[0]
debug_transport = devices[0].find_debug()
# Creates object for manipulating TREZOR
debug_transport = HidTransport(devices[0], **{'debug_link': True})
client = TrezorClient(transport)
debug = DebugLink(debug_transport)
@ -36,7 +34,7 @@ def main():
arg2 = int(sys.argv[2], 16)
step = 0x400 if arg2 >= 0x400 else arg2
f = open('memory.dat', 'w')
f = open('memory.dat', 'wb')
for addr in range(arg1, arg1 + arg2, step):
mem = debug.memory_read(addr, step)

View File

@ -1,16 +1,14 @@
#!/usr/bin/env python
from __future__ import print_function
#!/usr/bin/env python3
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:
@ -18,10 +16,10 @@ def main():
return
# Use first connected device
transport = HidTransport(devices[0])
transport = devices[0]
debug_transport = devices[0].find_debug()
# Creates object for manipulating TREZOR
debug_transport = HidTransport(devices[0], **{'debug_link': True})
client = TrezorClient(transport)
debug = DebugLink(debug_transport)

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python
from __future__ import print_function
#!/usr/bin/env python3
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):
'''
@ -29,25 +22,25 @@ def generate_entropy(strength, internal_entropy, external_entropy):
random - binary stream of random data from external HRNG
'''
if strength not in (128, 192, 256):
raise Exception("Invalid strength")
raise ValueError("Invalid strength")
if not internal_entropy:
raise Exception("Internal entropy is not provided")
raise ValueError("Internal entropy is not provided")
if len(internal_entropy) < 32:
raise Exception("Internal entropy too short")
raise ValueError("Internal entropy too short")
if not external_entropy:
raise Exception("External entropy is not provided")
raise ValueError("External entropy is not provided")
if len(external_entropy) < 32:
raise Exception("External entropy too short")
raise ValueError("External entropy too short")
entropy = hashlib.sha256(internal_entropy + external_entropy).digest()
entropy_stripped = entropy[:strength // 8]
if len(entropy_stripped) * 8 != strength:
raise Exception("Entropy length mismatch")
raise ValueError("Entropy length mismatch")
return entropy_stripped

216
tools/pb2py Executable file
View File

@ -0,0 +1,216 @@
#!/usr/bin/env python3
# Converts Google's protobuf python definitions of TREZOR wire messages
# to plain-python objects as used in TREZOR Core and python-trezor
import sys
import os
import argparse
from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper
def process_type(t, cls, msg_id, indexfile, is_upy):
print(" * type %s" % t)
imports = []
out = ["", "", "class %s(p.MessageType):" % t, ]
if cls.DESCRIPTOR.fields_by_name:
out.append(" FIELDS = {")
elif msg_id is None:
out.append(" pass")
for v in sorted(cls.DESCRIPTOR.fields_by_name.values(), key=lambda x: x.number):
number = v.number
fieldname = v.name
type = None
repeated = v.label == 3
required = v.label == 2
# print v.has_default_value, v.default_value
if v.type in (4, 13, 14):
# TYPE_UINT64 = 4
# TYPE_UINT32 = 13
# TYPE_ENUM = 14
type = 'p.UVarintType'
elif v.type in (17,):
# TYPE_SINT32 = 17
type = 'p.Sint32Type'
elif v.type in (18,):
# TYPE_SINT64 = 18
type = 'p.Sint64Type'
elif v.type == 9:
# TYPE_STRING = 9
type = 'p.UnicodeType'
elif v.type == 8:
# TYPE_BOOL = 8
type = 'p.BoolType'
elif v.type == 12:
# TYPE_BYTES = 12
type = 'p.BytesType'
elif v.type == 11:
# TYPE_MESSAGE = 1
type = v.message_type.name
imports.append("from .%s import %s" %
(v.message_type.name, v.message_type.name))
else:
raise Exception("Unknown field type %s for field %s" %
(v.type, fieldname))
if required:
comment = ' # required'
elif v.has_default_value:
comment = ' # default=%s' % repr(v.default_value)
else:
comment = ''
if repeated:
flags = 'p.FLAG_REPEATED'
else:
flags = '0'
out.append(" %d: ('%s', %s, %s),%s" %
(number, fieldname, type, flags, comment))
# print fieldname, number, type, repeated, comment
# print v.__dict__
# print v.CPPTYPE_STRING
# print v.LABEL_REPEATED
# print v.enum_type
# v.has_default_value, v.default_value
# v.label == 3 # repeated
# print v.number
if cls.DESCRIPTOR.fields_by_name:
out.append(" }")
if msg_id is not None:
out.append(" MESSAGE_WIRE_TYPE = %d" % msg_id)
if indexfile is not None:
if is_upy:
indexfile.write("%s = const(%d)\n" % (t, msg_id))
else:
indexfile.write("%s = %d\n" % (t, msg_id))
# Remove duplicate imports
imports = sorted(list(set(imports)))
if is_upy:
imports = ['import protobuf as p'] + imports
else:
imports = ['from __future__ import absolute_import',
'from .. import protobuf as p'] + imports
return imports + out
def process_enum(t, cls, is_upy):
out = []
if is_upy:
out += ("from micropython import const", "")
print(" * enum %s" % t)
for k, v in cls.items():
# Remove type name from the beginning of the constant
# For example "PinMatrixRequestType_Current" -> "Current"
if k.startswith("%s_" % t):
k = k.replace("%s_" % t, '')
# If type ends with *Type, but constant use type name without *Type, remove it too :)
# For example "ButtonRequestType & ButtonRequest_Other" => "Other"
if t.endswith("Type") and k.startswith("%s_" % t.replace("Type", '')):
k = k.replace("%s_" % t.replace("Type", ''), '')
if is_upy:
out.append("%s = const(%s)" % (k, v))
else:
out.append("%s = %s" % (k, v))
return out
def find_msg_type(msg_types, t):
for k, v in msg_types:
msg_name = k.replace('MessageType_', '')
if msg_name == t:
return v
def process_module(mod, genpath, indexfile, modlist, is_upy):
print("Processing module %s" % mod.__name__)
types = dict([(name, cls)
for name, cls in mod.__dict__.items() if isinstance(cls, type)])
msg_types = __import__('pb2', globals(), locals(), [
'messages_pb2', ]).messages_pb2.MessageType.items()
for t, cls in sorted(types.items()):
# Find message type for given class
msg_id = find_msg_type(msg_types, t)
out = process_type(t, cls, msg_id, indexfile, is_upy)
write_to_file(genpath, t, out)
if modlist:
modlist.write("from .%s import *\n" % t)
enums = dict([(name, cls) for name, cls in mod.__dict__.items()
if isinstance(cls, EnumTypeWrapper)])
for t, cls in enums.items():
out = process_enum(t, cls, is_upy)
write_to_file(genpath, t, out)
if modlist:
modlist.write("from . import %s\n" % t)
def write_to_file(genpath, t, out):
# Write generated sourcecode to given file
f = open(os.path.join(genpath, "%s.py" % t), 'w')
out = ["# Automatically generated by pb2py"] + out
data = "\n".join(out) + "\n"
f.write(data)
f.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('modulename', type=str, help="Name of module to generate")
parser.add_argument('genpath', type=str, help="Directory for generated source code")
parser.add_argument('-i', '--indexfile', type=str, help="[optional] Generate index file of wire types")
parser.add_argument('-l', '--modlist', type=str, help="[optional] Generate list of modules")
parser.add_argument('-p', '--protopath', type=str, help="[optional] Path to search for pregenerated Google's python sources")
parser.add_argument('-m', '--micropython', action='store_true', help="Use micropython-favoured source code")
args = parser.parse_args()
if args.indexfile:
indexfile = open(args.indexfile, 'a')
else:
indexfile = None
if args.modlist:
modlist = open(args.modlist, 'a')
else:
modlist = None
if args.protopath:
sys.path.append(args.protopath)
# Dynamically load module from argv[1]
tmp = __import__('pb2', globals(), locals(), ['%s_pb2' % args.modulename])
mod = getattr(tmp, "%s_pb2" % args.modulename)
process_module(mod, args.genpath, indexfile, modlist, args.micropython)

176
tools/pwd_reader.py Executable file
View File

@ -0,0 +1,176 @@
#!/usr/bin/env python3
from binascii import hexlify, unhexlify
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import hmac
import hashlib
import json
import os
from urllib.parse import urlparse
from trezorlib.client import TrezorClient
from trezorlib.transport import get_transport
# Return path by BIP-32
def getPath(client):
return client.expand_path("10016'/0")
# Deriving master key
def getMasterKey(client):
bip32_path = getPath(client)
ENC_KEY = 'Activate TREZOR Password Manager?'
ENC_VALUE = unhexlify('2d650551248d792eabf628f451200d7f51cb63e46aadcbb1038aacb05e8c8aee2d650551248d792eabf628f451200d7f51cb63e46aadcbb1038aacb05e8c8aee')
key = hexlify(client.encrypt_keyvalue(
bip32_path,
ENC_KEY,
ENC_VALUE,
True,
True
))
return key
# Deriving file name and encryption key
def getFileEncKey(key):
filekey, enckey = key[:len(key) // 2], key[len(key) // 2:]
FILENAME_MESS = b'5f91add3fa1c3c76e90c90a3bd0999e2bd7833d06a483fe884ee60397aca277a'
digest = hmac.new(filekey, FILENAME_MESS, hashlib.sha256).hexdigest()
filename = digest + '.pswd'
return [filename, filekey, enckey]
# File level decryption and file reading
def decryptStorage(path, key):
cipherkey = unhexlify(key)
with open(path, 'rb') as f:
iv = f.read(12)
tag = f.read(16)
cipher = Cipher(algorithms.AES(cipherkey), modes.GCM(iv, tag), backend=default_backend())
decryptor = cipher.decryptor()
data = ''
while True:
block = f.read(16)
# data are not authenticated yet
if block:
data = data + decryptor.update(block).decode()
else:
break
# throws exception when the tag is wrong
data = data + decryptor.finalize().decode()
return json.loads(data)
def decryptEntryValue(nonce, val):
cipherkey = unhexlify(nonce)
iv = val[:12]
tag = val[12:28]
cipher = Cipher(algorithms.AES(cipherkey), modes.GCM(iv, tag), backend=default_backend())
decryptor = cipher.decryptor()
data = ''
inputData = val[28:]
while True:
block = inputData[:16]
inputData = inputData[16:]
if block:
data = data + decryptor.update(block).decode()
else:
break
# throws exception when the tag is wrong
data = data + decryptor.finalize().decode()
return json.loads(data)
# Decrypt give entry nonce
def getDecryptedNonce(client, entry):
print()
print('Waiting for TREZOR input ...')
print()
if 'item' in entry:
item = entry['item']
else:
item = entry['title']
pr = urlparse(item)
if pr.scheme and pr.netloc:
item = pr.netloc
ENC_KEY = 'Unlock %s for user %s?' % (item, entry['username'])
ENC_VALUE = entry['nonce']
decrypted_nonce = hexlify(client.decrypt_keyvalue(
getPath(client),
ENC_KEY,
unhexlify(ENC_VALUE),
False,
True
))
return decrypted_nonce
# Pretty print of list
def printEntries(entries):
print('Password entries')
print('================')
print()
for k, v in entries.items():
print('Entry id: #%s' % k)
print('-------------')
for kk, vv in v.items():
if kk in ['nonce', 'safe_note', 'password']:
continue # skip these fields
print('*', kk, ': ', vv)
print()
return
def main():
try:
transport = get_transport()
except Exception as e:
print(e)
return
client = TrezorClient(transport)
print()
print('Confirm operation on TREZOR')
print()
masterKey = getMasterKey(client)
# print('master key:', masterKey)
fileName = getFileEncKey(masterKey)[0]
# print('file name:', fileName)
path = os.path.expanduser('~/Dropbox/Apps/TREZOR Password Manager/')
# print('path to file:', path)
encKey = getFileEncKey(masterKey)[2]
# print('enckey:', encKey)
full_path = path + fileName
parsed_json = decryptStorage(full_path, encKey)
# list entries
entries = parsed_json['entries']
printEntries(entries)
entry_id = input('Select entry number to decrypt: ')
entry_id = str(entry_id)
plain_nonce = getDecryptedNonce(client, entries[entry_id])
pwdArr = entries[entry_id]['password']['data']
pwdHex = ''.join([hex(x)[2:].zfill(2) for x in pwdArr])
print('password: ', decryptEntryValue(plain_nonce, unhexlify(pwdHex)))
safeNoteArr = entries[entry_id]['safe_note']['data']
safeNoteHex = ''.join([hex(x)[2:].zfill(2) for x in safeNoteArr])
print('safe_note:', decryptEntryValue(plain_nonce, unhexlify(safeNoteHex)))
return
if __name__ == '__main__':
main()

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# example usage: ./rng_entropy_collector.py stm32_rng_1.dat 1048576
# note: for reading large amounts of entropy, compile a firmware
# that has DEBUG_RNG == 1 as that will disable the user button
@ -8,21 +8,14 @@ from __future__ import print_function
import io
import sys
from trezorlib.client import TrezorClient
from trezorlib.transport_hid import HidTransport
def get_client():
devices = HidTransport.enumerate() # list all connected TREZORs on USB
if len(devices) == 0: # check whether we found any
return None
transport = HidTransport(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
@ -30,7 +23,7 @@ def main():
step = 1024 if arg2 >= 1024 else arg2 # trezor will only return 1KB at a time
with io.open(arg1, 'wb') as f:
for i in xrange(0, arg2, step):
for i in range(0, arg2, step):
entropy = client.get_entropy(step)
f.write(entropy)

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python
from __future__ import print_function
import binascii
import os
import random
@ -10,11 +9,9 @@ import trezorlib.ckd_public as bip32
import hashlib
from trezorlib.client import TrezorClient
from trezorlib.client import TrezorClientDebug
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):
@ -153,18 +150,14 @@ 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
print(devices[0][0])
# transport = BridgeTransport(devices[0][0])
transport = HidTransport(devices[0])
print(transport)
txstore = MyTxApiBitcoin()

View File

@ -1,114 +0,0 @@
#!/usr/bin/env python2
#
# Copyright (C) 2017 mruddy
#
# 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 print_function
import binascii
from trezorlib.client import TrezorClient
from trezorlib.transport_hid import HidTransport
from trezorlib.tx_api import TxApiBitcoin, TxApiTestnet, TxApiLitecoin
from trezorlib import types_pb2 as types
# Python2 vs Python3
try:
input = raw_input
except NameError:
pass
def get_client():
devices = HidTransport.enumerate() # list all connected TREZORs on USB
if len(devices) == 0: # check whether we found any
return None
transport = HidTransport(devices[0]) # use first connected device
return TrezorClient(transport) # creates object for communicating with TREZOR
def get_txapi():
coin = input('Which coin {Bitcoin, Testnet, Litecoin}? ').strip()
if coin not in {'Bitcoin', 'Testnet', 'Litecoin'}:
return None, None
txapi_lookup = {
'Bitcoin': TxApiBitcoin,
'Testnet': TxApiTestnet,
'Litecoin': TxApiLitecoin
}
return coin, txapi_lookup[coin]
def main():
client = get_client()
if not client:
print('No TREZOR connected')
return
print()
print('Welcome to the user-unfriendly transaction signing tool')
print('USE AT YOUR OWN RISK!!!')
print()
coin, txapi = get_txapi()
if not txapi:
print('Coin not supported')
return
client.set_tx_api(txapi)
inputs = []
while True:
print()
prev_in_hash = input('Previous input hash (empty to move on): ').strip()
if prev_in_hash == '':
break
prev_in_vout = input('Previous input index: ').strip()
addrn = input("Node path to sign with (e.g.- %s/0'/0/0): " % coin).strip()
inputs.append(types.TxInputType(
prev_hash=binascii.unhexlify(prev_in_hash),
prev_index=int(prev_in_vout, 10),
address_n=client.expand_path(addrn)
))
outputs = []
while True:
print()
out_addr = input('Pay to address (empty to move on): ').strip()
if out_addr == '':
break
out_amount = input('Amount (in satoshis): ').strip()
outputs.append(types.TxOutputType(
amount=int(out_amount, 10),
script_type=types.PAYTOADDRESS,
address=out_addr
))
(signatures, serialized_tx) = client.sign_tx(coin, inputs, outputs)
client.close()
print()
print('Signed Transaction:', binascii.hexlify(serialized_tx))
# note: these api's are useful for checking and sending the output of this tool:
# https://btc.blockr.io/tx/push -or- https://live.blockcypher.com/btc/pushtx/
# https://tbtc.blockr.io/tx/push -or- https://live.blockcypher.com/btc-testnet/pushtx/
# https://ltc.blockr.io/tx/push -or - https://live.blockcypher.com/ltc/pushtx/
if __name__ == '__main__':
main()

View File

@ -1,6 +1,6 @@
[tox]
envlist =
py27,
py33,
py34,
py35,
py36,
@ -8,6 +8,9 @@ envlist =
[testenv]
deps =
-rrequirements.txt
pytest
mock
commands =
python -m compileall trezorlib/
python trezorctl --help
python -m pytest --pyarg trezorlib.tests.unit_tests

1388
trezorctl

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
__version__ = '0.9.1'

View File

@ -28,24 +28,17 @@ from ecdsa.curves import SECP256k1
from ecdsa.ellipticcurve import Point, INFINITY
from . import tools
from . import types_pb2 as proto_types
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):
@ -53,7 +46,7 @@ def sec_to_public_pair(pubkey):
x = string_to_number(pubkey[1:33])
sec0 = pubkey[:1]
if sec0 not in (b'\2', b'\3'):
raise Exception("Compressed pubkey expected")
raise ValueError("Compressed pubkey expected")
def public_pair_for_x(generator, x, is_even):
curve = generator.curve()
@ -81,9 +74,9 @@ def get_address(public_node, address_type):
def public_ckd(public_node, n):
if not isinstance(n, list):
raise Exception('Parameter must be a list')
raise ValueError('Parameter must be a list')
node = proto_types.HDNodeType()
node = proto.HDNodeType()
node.CopyFrom(public_node)
for i in n:
@ -97,7 +90,7 @@ def get_subnode(node, i):
i_as_bytes = struct.pack(">L", i)
if is_prime(i):
raise Exception("Prime derivation not supported")
raise ValueError("Prime derivation not supported")
# Public derivation
data = node.public_key + i_as_bytes
@ -105,7 +98,7 @@ def get_subnode(node, i):
I64 = hmac.HMAC(key=node.chain_code, msg=data, digestmod=hashlib.sha512).digest()
I_left_as_exponent = string_to_number(I64[:32])
node_out = proto_types.HDNodeType()
node_out = proto.HDNodeType()
node_out.depth = node.depth + 1
node_out.child_num = i
node_out.chain_code = I64[32:]
@ -116,7 +109,7 @@ def get_subnode(node, i):
point = I_left_as_exponent * SECP256k1.generator + Point(SECP256k1.curve, x, y, SECP256k1.order)
if point == INFINITY:
raise Exception("Point cannot be INFINITY")
raise ValueError("Point cannot be INFINITY")
# Convert public point to compressed public key
node_out.public_key = point_to_pubkey(point)
@ -143,16 +136,16 @@ def deserialize(xpub):
data = tools.b58decode(xpub, None)
if tools.Hash(data[:-4])[:4] != data[-4:]:
raise Exception("Checksum failed")
raise ValueError("Checksum failed")
node = proto_types.HDNodeType()
node = proto.HDNodeType()
node.depth = struct.unpack('>B', data[4:5])[0]
node.fingerprint = struct.unpack('>I', data[5:9])[0]
node.child_num = struct.unpack('>I', data[9:13])[0]
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

View File

@ -25,23 +25,22 @@ import time
import binascii
import hashlib
import unicodedata
# import json
import json
import getpass
import warnings
from mnemonic import Mnemonic
from . import messages as proto
from . import tools
# from . import mapping
from . import messages_pb2 as proto
from . import types_pb2 as types
from . import mapping
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:
raise Exception("Trezorlib does not support Python 2 anymore.")
# try:
# from PIL import Image
@ -51,25 +50,15 @@ except NameError:
SCREENSHOT = False
DEFAULT_CURVE = 'secp256k1'
# monkeypatching: text formatting of protobuf messages
tools.monkeypatch_google_protobuf_text_format()
def getch():
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
# make a getch function
try:
import termios
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,30 +68,70 @@ 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):
# Converts integer code to its string representation of ButtonRequestType
return [k for k, v in types.ButtonRequestType.items() if v == code][0]
return [k for k in dir(proto.ButtonRequestType) if getattr(proto.ButtonRequestType, k) == code][0]
def format_protobuf(pb, indent=0, sep=' ' * 4):
def pformat_value(value, indent):
level = sep * indent
leadin = sep * (indent + 1)
if isinstance(value, MessageType):
return format_protobuf(value, indent, sep)
if isinstance(value, list):
lines = []
lines.append('[')
lines += [leadin + pformat_value(x, indent + 1) + ',' for x in value]
lines.append(level + ']')
return '\n'.join(lines)
if isinstance(value, dict):
lines = []
lines.append('{')
for key, val in sorted(value.items()):
if val is None or val == []:
continue
if key == 'address_n' and isinstance(val, list):
lines.append(leadin + key + ': ' + repr(val) + ',')
else:
lines.append(leadin + key + ': ' + pformat_value(val, indent + 1) + ',')
lines.append(level + '}')
return '\n'.join(lines)
if isinstance(value, bytearray):
return 'bytearray(0x{})'.format(binascii.hexlify(value).decode('ascii'))
return repr(value)
return pb.__class__.__name__ + ' ' + pformat_value(pb.__dict__, indent)
def pprint(msg):
msg_class = msg.__class__.__name__
msg_size = msg.ByteSize()
"""
msg_ser = msg.SerializeToString()
msg_id = mapping.get_type(msg)
msg_json = json.dumps(protobuf_json.pb2json(msg))
"""
if isinstance(msg, proto.FirmwareUpload):
return "<%s> (%d bytes):\n" % (msg_class, msg_size)
if isinstance(msg, proto.FirmwareUpload) or isinstance(msg, proto.SelfTest) \
or isinstance(msg, proto.Features):
return "<%s> (%d bytes)" % (msg_class, msg_size)
else:
return "<%s> (%d bytes):\n%s" % (msg_class, msg_size, msg)
return "<%s> (%d bytes):\n%s" % (msg_class, msg_size, format_protobuf(msg))
def log(msg):
sys.stderr.write(str(msg))
sys.stderr.write(msg)
sys.stderr.write('\n')
sys.stderr.flush()
@ -113,6 +142,11 @@ class CallException(Exception):
self.args = [code, message]
class AssertionException(Exception):
def __init__(self, code, message):
self.args = [code, message]
class PinException(CallException):
pass
@ -127,7 +161,6 @@ class field(object):
def __call__(self, f):
def wrapped_f(*args, **kwargs):
ret = f(*args, **kwargs)
ret.HasField(self.field)
return getattr(ret, self.field)
return wrapped_f
@ -143,7 +176,7 @@ class expect(object):
def wrapped_f(*args, **kwargs):
ret = f(*args, **kwargs)
if not isinstance(ret, self.expected):
raise Exception("Got %s, expected %s" % (ret.__class__, self.expected))
raise RuntimeError("Got %s, expected %s" % (ret.__class__, self.expected))
return ret
return wrapped_f
@ -153,8 +186,8 @@ def session(f):
# with session activation / deactivation
def wrapped_f(*args, **kwargs):
client = args[0]
client.transport.session_begin()
try:
client.transport.session_begin()
return f(*args, **kwargs)
finally:
client.transport.session_end()
@ -162,18 +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:
if isinstance(txt, bytes):
return unicodedata.normalize('NFC', txt.decode('utf-8'))
if isinstance(txt, str):
return unicodedata.normalize('NFC', txt)
raise Exception('unicode/str or bytes/str expected')
'''
Normalize message to NFC and return bytes suitable for protobuf.
This seems to be bitcoin-qt standard of doing things.
'''
if isinstance(txt, bytes):
txt = txt.decode('utf-8')
return unicodedata.normalize('NFC', txt).encode('utf-8')
class BaseClient(object):
@ -183,13 +211,16 @@ class BaseClient(object):
self.transport = transport
super(BaseClient, self).__init__() # *args, **kwargs)
def close(self):
pass
def cancel(self):
self.transport.write(proto.Cancel())
@session
def call_raw(self, msg):
self.transport.write(msg)
return self.transport.read_blocking()
return self.transport.read()
@session
def call(self, msg):
@ -200,26 +231,27 @@ class BaseClient(object):
if handler is not None:
msg = handler(resp)
if msg is None:
raise Exception("Callback %s must return protobuf message, not None" % handler)
raise ValueError("Callback %s must return protobuf message, not None" % handler)
resp = self.call(msg)
return resp
def callback_Failure(self, msg):
if msg.code in (types.Failure_PinInvalid,
types.Failure_PinCancelled, types.Failure_PinExpected):
if msg.code in (proto.FailureType.PinInvalid,
proto.FailureType.PinCancelled, proto.FailureType.PinExpected):
raise PinException(msg.code, msg.message)
raise CallException(msg.code, msg.message)
def close(self):
self.transport.close()
def register_message(self, msg):
'''Allow application to register custom protobuf message type'''
mapping.register_message(msg)
class DebugWireMixin(object):
class VerboseWireMixin(object):
def call_raw(self, msg):
log("SENDING " + pprint(msg))
resp = super(DebugWireMixin, self).call_raw(msg)
resp = super(VerboseWireMixin, self).call_raw(msg)
log("RECEIVED " + pprint(resp))
return resp
@ -241,7 +273,8 @@ class TextUIMixin(object):
def callback_RecoveryMatrix(self, msg):
if self.recovery_matrix_first_pass:
self.recovery_matrix_first_pass = False
log("Use the numeric keypad to describe positions. For the word list use only left and right keys. The layout is:")
log("Use the numeric keypad to describe positions. For the word list use only left and right keys.")
log("Use backspace to correct an entry. The keypad layout is:")
log(" 7 8 9 7 | 9")
log(" 4 5 6 4 | 6")
log(" 1 2 3 1 | 3")
@ -254,18 +287,18 @@ class TextUIMixin(object):
return proto.WordAck(word='\x08')
# ignore middle column if only 6 keys requested.
if (isinstance(msg.type, types.WordRequestType_Matrix6) and character in ('2', '5', '8')):
if msg.type == proto.WordRequestType.Matrix6 and character in ('2', '5', '8'):
continue
if (ord(character) >= ord('1') and ord(character) <= ord('9')):
if character.isdigit():
return proto.WordAck(word=character)
def callback_PinMatrixRequest(self, msg):
if msg.type == 1:
if msg.type == proto.PinMatrixRequestType.Current:
desc = 'current PIN'
elif msg.type == 2:
elif msg.type == proto.PinMatrixRequestType.NewFirst:
desc = 'new PIN'
elif msg.type == 3:
elif msg.type == proto.PinMatrixRequestType.NewSecond:
desc = 'new PIN again'
else:
desc = 'PIN'
@ -276,22 +309,35 @@ class TextUIMixin(object):
log(" 1 2 3")
log("Please enter %s: " % desc)
pin = getpass.getpass('')
if not pin.isdigit():
raise ValueError('Non-numerical PIN provided')
return proto.PinMatrixAck(pin=pin)
def callback_PassphraseRequest(self, msg):
if msg.on_device is True:
return proto.PassphraseAck()
if os.getenv("PASSPHRASE") is not None:
log("Passphrase required. Using PASSPHRASE environment variable.")
passphrase = Mnemonic.normalize_string(os.getenv("PASSPHRASE"))
return proto.PassphraseAck(passphrase=passphrase)
log("Passphrase required: ")
passphrase = getpass.getpass('')
log("Confirm your Passphrase: ")
if passphrase == getpass.getpass(''):
passphrase = normalize_nfc(passphrase)
passphrase = Mnemonic.normalize_string(passphrase)
return proto.PassphraseAck(passphrase=passphrase)
else:
log("Passphrase did not match! ")
exit()
def callback_PassphraseStateRequest(self, msg):
return proto.PassphraseStateAck()
def callback_WordRequest(self, msg):
if msg.type in (types.WordRequestType_Matrix9,
types.WordRequestType_Matrix6):
if msg.type in (proto.WordRequestType.Matrix9,
proto.WordRequestType.Matrix6):
return self.callback_RecoveryMatrix(msg)
log("Enter one word of mnemonic: ")
word = input()
@ -353,8 +399,8 @@ class DebugLinkMixin(object):
# return isinstance(value, TypeError)
# Evaluate missed responses in 'with' statement
if self.expected_responses is not None and len(self.expected_responses):
raise Exception("Some of expected responses didn't come from device: %s" %
[pprint(x) for x in self.expected_responses])
raise RuntimeError("Some of expected responses didn't come from device: %s" %
[pprint(x) for x in self.expected_responses])
# Cleanup
self.expected_responses = None
@ -362,7 +408,7 @@ class DebugLinkMixin(object):
def set_expected_responses(self, expected):
if not self.in_with_statement:
raise Exception("Must be called inside 'with' statement")
raise RuntimeError("Must be called inside 'with' statement")
self.expected_responses = expected
def setup_debuglink(self, button, pin_correct):
@ -370,10 +416,10 @@ class DebugLinkMixin(object):
self.pin_correct = pin_correct
def set_passphrase(self, passphrase):
self.passphrase = normalize_nfc(passphrase)
self.passphrase = Mnemonic.normalize_string(passphrase)
def set_mnemonic(self, mnemonic):
self.mnemonic = normalize_nfc(mnemonic).split(' ')
self.mnemonic = Mnemonic.normalize_string(mnemonic).split(' ')
def call_raw(self, msg):
@ -398,18 +444,19 @@ class DebugLinkMixin(object):
try:
expected = self.expected_responses.pop(0)
except IndexError:
raise CallException(types.Failure_UnexpectedMessage,
"Got %s, but no message has been expected" % pprint(msg))
raise AssertionException(proto.FailureType.UnexpectedMessage,
"Got %s, but no message has been expected" % pprint(msg))
if msg.__class__ != expected.__class__:
raise CallException(types.Failure_UnexpectedMessage,
"Expected %s, got %s" % (pprint(expected), pprint(msg)))
raise AssertionException(proto.FailureType.UnexpectedMessage,
"Expected %s, got %s" % (pprint(expected), pprint(msg)))
fields = expected.ListFields() # only filled (including extensions)
for field, value in fields:
if not msg.HasField(field.name) or getattr(msg, field.name) != value:
raise CallException(types.Failure_UnexpectedMessage,
"Expected %s, got %s" % (pprint(expected), pprint(msg)))
for field, value in expected.__dict__.items():
if value is None or value == []:
continue
if getattr(msg, field) != value:
raise AssertionException(proto.FailureType.UnexpectedMessage,
"Expected %s, got %s" % (pprint(expected), pprint(msg)))
def callback_ButtonRequest(self, msg):
log("ButtonRequest code: " + get_buttonrequest_value(msg.code))
@ -432,6 +479,9 @@ class DebugLinkMixin(object):
log("Provided passphrase: '%s'" % self.passphrase)
return proto.PassphraseAck(passphrase=self.passphrase)
def callback_PassphraseStateRequest(self, msg):
return proto.PassphraseStateAck()
def callback_WordRequest(self, msg):
(word, pos) = self.debug.read_recovery_word()
if word != '':
@ -439,7 +489,7 @@ class DebugLinkMixin(object):
if pos != 0:
return proto.WordAck(word=self.mnemonic[pos - 1])
raise Exception("Unexpected call")
raise RuntimeError("Unexpected call")
class ProtocolMixin(object):
@ -457,7 +507,7 @@ class ProtocolMixin(object):
def init_device(self):
self.features = expect(proto.Features)(self.call)(proto.Initialize())
if str(self.features.vendor) not in self.VENDORS:
raise Exception("Unsupported device")
raise RuntimeError("Unsupported device")
def _get_local_entropy(self):
return os.urandom(32)
@ -480,20 +530,8 @@ class ProtocolMixin(object):
n = n[1:]
# coin_name/a/b/c => 44'/SLIP44_constant'/a/b/c
coins = {
"Bitcoin": 0,
"Testnet": 1,
"Namecoin": 7,
"Litecoin": 2,
"Dogecoin": 3,
"Dash": 5,
"Ether": 60,
"EtherClassic": 61,
"Zcash": 133,
"Decred": 42
}
if n[0] in coins:
n = ["44'", "%d'" % coins[n[0]]] + n[1:]
if n[0] in coins_slip44:
n = ["44'", "%d'" % coins_slip44[n[0]]] + n[1:]
path = []
for x in n:
@ -514,15 +552,13 @@ class ProtocolMixin(object):
return path
@expect(proto.PublicKey)
def get_public_node(self, n, ecdsa_curve_name=DEFAULT_CURVE, show_display=False, coin_name=None):
def get_public_node(self, n, ecdsa_curve_name=None, show_display=False, coin_name=None):
n = self._convert_prime(n)
if not ecdsa_curve_name:
ecdsa_curve_name = DEFAULT_CURVE
return self.call(proto.GetPublicKey(address_n=n, ecdsa_curve_name=ecdsa_curve_name, show_display=show_display, coin_name=coin_name))
@field('address')
@expect(proto.Address)
def get_address(self, coin_name, n, show_display=False, multisig=None, script_type=types.SPENDADDRESS):
def get_address(self, coin_name, n, show_display=False, multisig=None, script_type=proto.InputScriptType.SPENDADDRESS):
n = self._convert_prime(n)
if multisig:
return self.call(proto.GetAddress(address_n=n, coin_name=coin_name, show_display=show_display, multisig=multisig, script_type=script_type))
@ -565,13 +601,29 @@ class ProtocolMixin(object):
response = self.call(msg)
while response.HasField('data_length'):
while response.data_length is not None:
data_length = response.data_length
data, chunk = data[data_length:], data[:data_length]
response = self.call(proto.EthereumTxAck(data_chunk=chunk))
return response.signature_v, response.signature_r, response.signature_s
@expect(proto.EthereumMessageSignature)
def ethereum_sign_message(self, n, message):
n = self._convert_prime(n)
message = normalize_nfc(message)
return self.call(proto.EthereumSignMessage(address_n=n, message=message))
def ethereum_verify_message(self, address, signature, message):
message = normalize_nfc(message)
try:
resp = self.call(proto.EthereumVerifyMessage(address=address, signature=signature, message=message))
except CallException as e:
resp = e
if isinstance(resp, proto.Success):
return True
return False
@field('entropy')
@expect(proto.Entropy)
def get_entropy(self, size):
@ -606,6 +658,13 @@ class ProtocolMixin(object):
self.init_device() # Reload Features
return out
@field('message')
@expect(proto.Success)
def apply_flags(self, flags):
out = self.call(proto.ApplyFlags(flags=flags))
self.init_device() # Reload Features
return out
@field('message')
@expect(proto.Success)
def clear_session(self):
@ -619,29 +678,162 @@ class ProtocolMixin(object):
return ret
@expect(proto.MessageSignature)
def sign_message(self, coin_name, n, message):
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")
return self.call(proto.SignMessage(coin_name=coin_name, address_n=n, message=message))
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)
def sign_identity(self, identity, challenge_hidden, challenge_visual, ecdsa_curve_name=DEFAULT_CURVE):
def sign_identity(self, identity, challenge_hidden, challenge_visual, ecdsa_curve_name=None):
return self.call(proto.SignIdentity(identity=identity, challenge_hidden=challenge_hidden, challenge_visual=challenge_visual, ecdsa_curve_name=ecdsa_curve_name))
@expect(proto.ECDHSessionKey)
def get_ecdh_session_key(self, identity, peer_public_key, ecdsa_curve_name=DEFAULT_CURVE):
def get_ecdh_session_key(self, identity, peer_public_key, ecdsa_curve_name=None):
return self.call(proto.GetECDHSessionKey(identity=identity, peer_public_key=peer_public_key, ecdsa_curve_name=ecdsa_curve_name))
@expect(proto.CosiCommitment)
def cosi_commit(self, n, data):
n = self._convert_prime(n)
return self.call(proto.CosiCommit(address_n=n, data=data))
@expect(proto.CosiSignature)
def cosi_sign(self, n, data, global_commitment, global_pubkey):
n = self._convert_prime(n)
return self.call(proto.CosiSign(address_n=n, data=data, global_commitment=global_commitment, global_pubkey=global_pubkey))
@field('message')
@expect(proto.Success)
def set_u2f_counter(self, u2f_counter):
ret = self.call(proto.SetU2FCounter(u2f_counter=u2f_counter))
return ret
@field("address")
@expect(proto.NEMAddress)
def nem_get_address(self, n, network, show_display=False):
n = self._convert_prime(n)
return self.call(proto.NEMGetAddress(address_n=n, network=network, show_display=show_display))
@expect(proto.NEMSignedTx)
def nem_sign_tx(self, n, transaction):
n = self._convert_prime(n)
def common_to_proto(common):
msg = proto.NEMTransactionCommon()
msg.network = (common["version"] >> 24) & 0xFF
msg.timestamp = common["timeStamp"]
msg.fee = common["fee"]
msg.deadline = common["deadline"]
if "signed" in common:
msg.signer = binascii.unhexlify(common["signer"])
return msg
def transfer_to_proto(transfer):
msg = proto.NEMTransfer()
msg.recipient = transfer["recipient"]
msg.amount = transfer["amount"]
if "payload" in transfer["message"]:
msg.payload = binascii.unhexlify(transfer["message"]["payload"])
if transfer["message"]["type"] == 0x02:
msg.public_key = binascii.unhexlify(transfer["message"]["publicKey"])
if "mosaics" in transfer:
msg._extend_mosaics(proto.NEMMosaic(
namespace=mosaic["mosaicId"]["namespaceId"],
mosaic=mosaic["mosaicId"]["name"],
quantity=mosaic["quantity"],
) for mosaic in transfer["mosaics"])
return msg
def aggregate_modification_to_proto(aggregate_modification, msg):
msg._extend_modifications(proto.NEMCosignatoryModification(
type=modification["modificationType"],
public_key=binascii.unhexlify(modification["cosignatoryAccount"]),
) for modification in aggregate_modification["modifications"])
if "minCosignatories" in aggregate_modification:
msg.relative_change = aggregate_modification["minCosignatories"]["relativeChange"]
def provision_namespace_to_proto(provision_namespace, msg):
msg.namespace = provision_namespace["newPart"]
if provision_namespace["parent"]:
msg.parent = provision_namespace["parent"]
msg.sink = provision_namespace["rentalFeeSink"]
msg.fee = provision_namespace["rentalFee"]
def mosaic_creation_to_proto(mosaic_creation):
msg = proto.NEMMosaicCreation()
msg.definition.namespace = mosaic_creation["mosaicDefinition"]["id"]["namespaceId"]
msg.definition.mosaic = mosaic_creation["mosaicDefinition"]["id"]["name"]
if mosaic_creation["mosaicDefinition"]["levy"]:
msg.definition.levy = mosaic_creation["mosaicDefinition"]["levy"]["type"]
msg.definition.fee = mosaic_creation["mosaicDefinition"]["levy"]["fee"]
msg.definition.levy_address = mosaic_creation["mosaicDefinition"]["levy"]["recipient"]
msg.definition.levy_namespace = mosaic_creation["mosaicDefinition"]["levy"]["mosaicId"]["namespaceId"]
msg.definition.levy_mosaic = mosaic_creation["mosaicDefinition"]["levy"]["mosaicId"]["name"]
msg.definition.description = mosaic_creation["mosaicDefinition"]["description"]
for property in mosaic_creation["mosaicDefinition"]["properties"]:
name = property["name"]
value = json.loads(property["value"])
if name == "divisibility":
msg.definition.divisibility = value
elif name == "initialSupply":
msg.definition.supply = value
elif name == "supplyMutable":
msg.definition.mutable_supply = value
elif name == "transferable":
msg.definition.transferable = value
msg.sink = mosaic_creation["creationFeeSink"]
msg.fee = mosaic_creation["creationFee"]
return msg
def mosaic_supply_change_to_proto(mosaic_supply_change):
msg = proto.NEMMosaicSupplyChange()
msg.namespace = mosaic_supply_change["mosaicId"]["namespaceId"]
msg.mosaic = mosaic_supply_change["mosaicId"]["name"]
msg.type = mosaic_supply_change["supplyType"]
msg.delta = mosaic_supply_change["delta"]
return msg
msg = proto.NEMSignTx()
msg.transaction = common_to_proto(transaction)
msg.transaction._extend_address_n(n)
msg.cosigning = (transaction["type"] == 0x1002)
if msg.cosigning or transaction["type"] == 0x1004:
transaction = transaction["otherTrans"]
msg.multisig = common_to_proto(transaction)
elif "otherTrans" in transaction:
raise CallException("Transaction does not support inner transaction")
if transaction["type"] == 0x0101:
msg.transfer = transfer_to_proto(transaction)
elif transaction["type"] == 0x1001:
aggregate_modification_to_proto(transaction, msg.aggregate_modification)
elif transaction["type"] == 0x2001:
provision_namespace_to_proto(transaction, msg.provision_namespace)
elif transaction["type"] == 0x4001:
msg = mosaic_creation_to_proto(transaction)
elif transaction["type"] == 0x4002:
msg.mosaic_supply_change = mosaic_supply_change_to_proto(transaction)
else:
raise CallException("Unknown transaction type")
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:
@ -687,46 +879,14 @@ class ProtocolMixin(object):
ask_on_decrypt=ask_on_decrypt,
iv=iv))
@field('tx_size')
@expect(proto.TxSize)
def estimate_tx_size(self, coin_name, inputs, outputs):
msg = proto.EstimateTxSize()
msg.coin_name = coin_name
msg.inputs_count = len(inputs)
msg.outputs_count = len(outputs)
return self.call(msg)
def _prepare_simple_sign_tx(self, coin_name, inputs, outputs):
msg = proto.SimpleSignTx()
msg.coin_name = coin_name
msg.inputs.extend(inputs)
msg.outputs.extend(outputs)
known_hashes = []
for inp in inputs:
if inp.prev_hash in known_hashes:
continue
tx = msg.transactions.add()
if self.tx_api:
tx.CopyFrom(self.tx_api.get_tx(binascii.hexlify(inp.prev_hash).decode('utf-8')))
else:
raise Exception('TX_API not defined')
known_hashes.append(inp.prev_hash)
return msg
def simple_sign_tx(self, coin_name, inputs, outputs):
msg = self._prepare_simple_sign_tx(coin_name, inputs, outputs)
return self.call(msg).serialized.serialized_tx
def _prepare_sign_tx(self, coin_name, inputs, outputs):
tx = types.TransactionType()
tx.inputs.extend(inputs)
tx.outputs.extend(outputs)
tx = proto.TransactionType()
tx._extend_inputs(inputs)
tx._extend_outputs(outputs)
tx._fill_missing()
txes = {}
txes[b''] = tx
txes[None] = tx
known_hashes = []
for inp in inputs:
@ -735,8 +895,9 @@ class ProtocolMixin(object):
if self.tx_api:
txes[inp.prev_hash] = self.tx_api.get_tx(binascii.hexlify(inp.prev_hash).decode('utf-8'))
txes[inp.prev_hash]._fill_missing()
else:
raise Exception('TX_API not defined')
raise RuntimeError('TX_API not defined')
known_hashes.append(inp.prev_hash)
return txes
@ -773,24 +934,27 @@ class ProtocolMixin(object):
raise CallException("Unexpected message")
# If there's some part of signed transaction, let's add it
if res.HasField('serialized') and res.serialized.HasField('serialized_tx'):
log("RECEIVED PART OF SERIALIZED TX (%d BYTES)" % len(res.serialized.serialized_tx))
if res.serialized and res.serialized.serialized_tx:
# log("RECEIVED PART OF SERIALIZED TX (%d BYTES)" % len(res.serialized.serialized_tx))
serialized_tx += res.serialized.serialized_tx
if res.HasField('serialized') and res.serialized.HasField('signature_index'):
if res.serialized and res.serialized.signature_index is not None:
if signatures[res.serialized.signature_index] is not None:
raise Exception("Signature for index %d already filled" % res.serialized.signature_index)
raise ValueError("Signature for index %d already filled" % res.serialized.signature_index)
signatures[res.serialized.signature_index] = res.serialized.signature
if res.request_type == types.TXFINISHED:
if res.request_type == proto.RequestType.TXFINISHED:
# Device didn't ask for more information, finish workflow
break
# Device asked for one more information, let's process it.
current_tx = txes[res.details.tx_hash]
if not res.details.tx_hash:
current_tx = txes[None]
else:
current_tx = txes[bytes(res.details.tx_hash)]
if res.request_type == types.TXMETA:
msg = types.TransactionType()
if res.request_type == proto.RequestType.TXMETA:
msg = proto.TransactionType()
msg.version = current_tx.version
msg.lock_time = current_tx.lock_time
msg.inputs_cnt = len(current_tx.inputs)
@ -798,44 +962,58 @@ class ProtocolMixin(object):
msg.outputs_cnt = len(current_tx.bin_outputs)
else:
msg.outputs_cnt = len(current_tx.outputs)
msg.extra_data_len = len(current_tx.extra_data)
msg.extra_data_len = len(current_tx.extra_data) if current_tx.extra_data else 0
res = self.call(proto.TxAck(tx=msg))
continue
elif res.request_type == types.TXINPUT:
msg = types.TransactionType()
msg.inputs.extend([current_tx.inputs[res.details.request_index], ])
res = self.call(proto.TxAck(tx=msg))
continue
elif res.request_type == types.TXOUTPUT:
msg = types.TransactionType()
if res.details.tx_hash:
msg.bin_outputs.extend([current_tx.bin_outputs[res.details.request_index], ])
else:
msg.outputs.extend([current_tx.outputs[res.details.request_index], ])
elif res.request_type == proto.RequestType.TXINPUT:
msg = proto.TransactionType()
msg._extend_inputs([current_tx.inputs[res.details.request_index], ])
if debug_processor is not None:
# msg needs to be deep copied so when it's modified
# the other messages stay intact
from copy import deepcopy
msg = deepcopy(msg)
# If debug_processor function is provided,
# pass thru it the request and prepared response.
# This is useful for unit tests, see test_msg_signtx
# This is useful for tests, see test_msg_signtx
msg = debug_processor(res, msg)
res = self.call(proto.TxAck(tx=msg))
continue
elif res.request_type == types.TXEXTRADATA:
elif res.request_type == proto.RequestType.TXOUTPUT:
msg = proto.TransactionType()
if res.details.tx_hash:
msg._extend_bin_outputs([current_tx.bin_outputs[res.details.request_index], ])
else:
msg._extend_outputs([current_tx.outputs[res.details.request_index], ])
if debug_processor is not None:
# msg needs to be deep copied so when it's modified
# the other messages stay intact
from copy import deepcopy
msg = deepcopy(msg)
# If debug_processor function is provided,
# pass thru it the request and prepared response.
# This is useful for tests, see test_msg_signtx
msg = debug_processor(res, msg)
res = self.call(proto.TxAck(tx=msg))
continue
elif res.request_type == proto.RequestType.TXEXTRADATA:
o, l = res.details.extra_data_offset, res.details.extra_data_len
msg = types.TransactionType()
msg = proto.TransactionType()
msg.extra_data = current_tx.extra_data[o:o + l]
res = self.call(proto.TxAck(tx=msg))
continue
if None in signatures:
raise Exception("Some signatures are missing!")
raise RuntimeError("Some signatures are missing!")
log("SIGNED IN %.03f SECONDS, CALLED %d MESSAGES, %d BYTES" %
(time.time() - start, counter, len(serialized_tx)))
# log("SIGNED IN %.03f SECONDS, CALLED %d MESSAGES, %d BYTES" %
# (time.time() - start, counter, len(serialized_tx)))
return (signatures, serialized_tx)
@ -848,12 +1026,12 @@ class ProtocolMixin(object):
@field('message')
@expect(proto.Success)
def recovery_device(self, word_count, passphrase_protection, pin_protection, label, language, type=types.RecoveryDeviceType_ScrambledWords, expand=False, dry_run=False):
def recovery_device(self, word_count, passphrase_protection, pin_protection, label, language, type=proto.RecoveryDeviceType.ScrambledWords, expand=False, dry_run=False):
if self.features.initialized and not dry_run:
raise Exception("Device is initialized already. Call wipe_device() and try again.")
raise RuntimeError("Device is initialized already. Call wipe_device() and try again.")
if word_count not in (12, 18, 24):
raise Exception("Invalid word count. Use 12/18/24")
raise ValueError("Invalid word count. Use 12/18/24")
self.recovery_matrix_first_pass = True
@ -878,9 +1056,9 @@ class ProtocolMixin(object):
@field('message')
@expect(proto.Success)
@session
def reset_device(self, display_random, strength, passphrase_protection, pin_protection, label, language, u2f_counter, skip_backup):
def reset_device(self, display_random, strength, passphrase_protection, pin_protection, label, language, u2f_counter=0, skip_backup=False):
if self.features.initialized:
raise Exception("Device is initialized already. Call wipe_device() and try again.")
raise RuntimeError("Device is initialized already. Call wipe_device() and try again.")
# Begin with device reset workflow
msg = proto.ResetDevice(display_random=display_random,
@ -894,10 +1072,10 @@ class ProtocolMixin(object):
resp = self.call(msg)
if not isinstance(resp, proto.EntropyRequest):
raise Exception("Invalid response, expected EntropyRequest")
raise RuntimeError("Invalid response, expected EntropyRequest")
external_entropy = self._get_local_entropy()
log("Computer generated entropy: " + binascii.hexlify(external_entropy).decode('ascii'))
log("Computer generated entropy: " + binascii.hexlify(external_entropy).decode())
ret = self.call(proto.EntropyAck(entropy=external_entropy))
self.init_device()
return ret
@ -910,12 +1088,12 @@ class ProtocolMixin(object):
@field('message')
@expect(proto.Success)
def load_device_by_mnemonic(self, mnemonic, pin, passphrase_protection, label, language, skip_checksum=False, expand=False):
def load_device_by_mnemonic(self, mnemonic, pin, passphrase_protection, label, language='english', skip_checksum=False, expand=False):
# Convert mnemonic to UTF8 NKFD
mnemonic = Mnemonic.normalize_string(mnemonic)
# Convert mnemonic to ASCII stream
mnemonic = normalize_nfc(mnemonic)
mnemonic = mnemonic.encode('utf-8')
m = Mnemonic('english')
@ -923,10 +1101,10 @@ class ProtocolMixin(object):
mnemonic = m.expand(mnemonic)
if not skip_checksum and not m.check(mnemonic):
raise Exception("Invalid mnemonic checksum")
raise ValueError("Invalid mnemonic checksum")
if self.features.initialized:
raise Exception("Device is initialized already. Call wipe_device() and try again.")
raise RuntimeError("Device is initialized already. Call wipe_device() and try again.")
resp = self.call(proto.LoadDevice(mnemonic=mnemonic, pin=pin,
passphrase_protection=passphrase_protection,
@ -940,23 +1118,23 @@ class ProtocolMixin(object):
@expect(proto.Success)
def load_device_by_xprv(self, xprv, pin, passphrase_protection, label, language):
if self.features.initialized:
raise Exception("Device is initialized already. Call wipe_device() and try again.")
raise RuntimeError("Device is initialized already. Call wipe_device() and try again.")
if xprv[0:4] not in ('xprv', 'tprv'):
raise Exception("Unknown type of xprv")
raise ValueError("Unknown type of xprv")
if len(xprv) < 100 and len(xprv) > 112:
raise Exception("Invalid length of xprv")
raise ValueError("Invalid length of xprv")
node = types.HDNodeType()
node = proto.HDNodeType()
data = binascii.hexlify(tools.b58decode(xprv, None))
if data[90:92] != b'00':
raise Exception("Contain invalid private key")
raise ValueError("Contain invalid private key")
checksum = binascii.hexlify(hashlib.sha256(hashlib.sha256(binascii.unhexlify(data[:156])).digest()).digest()[:4])
if checksum != data[156:]:
raise Exception("Checksum doesn't match")
raise ValueError("Checksum doesn't match")
# version 0488ade4
# depth 00
@ -983,12 +1161,12 @@ class ProtocolMixin(object):
@session
def firmware_update(self, fp):
if self.features.bootloader_mode is False:
raise Exception("Device must be in bootloader mode")
raise RuntimeError("Device must be in bootloader mode")
data = fp.read()
resp = self.call(proto.FirmwareErase(length=len(data)))
if isinstance(resp, proto.Failure) and resp.code == types.Failure_FirmwareError:
if isinstance(resp, proto.Failure) and resp.code == proto.FailureType.FirmwareError:
return False
# TREZORv1 method
@ -998,9 +1176,9 @@ class ProtocolMixin(object):
resp = self.call(proto.FirmwareUpload(payload=data))
if isinstance(resp, proto.Success):
return True
elif isinstance(resp, proto.Failure) and resp.code == types.Failure_FirmwareError:
elif isinstance(resp, proto.Failure) and resp.code == proto.FailureType.FirmwareError:
return False
raise Exception("Unexpected result %s" % resp)
raise RuntimeError("Unexpected result %s" % resp)
# TREZORv2 method
if isinstance(resp, proto.FirmwareRequest):
@ -1013,28 +1191,28 @@ class ProtocolMixin(object):
continue
elif isinstance(resp, proto.Success):
return True
elif isinstance(resp, proto.Failure) and resp.code == types.Failure_FirmwareError:
elif isinstance(resp, proto.Failure) and resp.code == proto.FailureType.FirmwareError:
return False
raise Exception("Unexpected result %s" % resp)
raise RuntimeError("Unexpected result %s" % resp)
raise Exception("Unexpected message %s" % resp)
raise RuntimeError("Unexpected message %s" % resp)
@field('message')
@expect(proto.Success)
def self_test(self):
if self.features.bootloader_mode is False:
raise Exception("Device must be in bootloader mode")
raise RuntimeError("Device must be in bootloader mode")
return self.call(proto.SelfTest())
return self.call(proto.SelfTest(payload=b'\x00\xFF\x55\xAA\x66\x99\x33\xCCABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\x00\xFF\x55\xAA\x66\x99\x33\xCC'))
class TrezorClient(ProtocolMixin, TextUIMixin, BaseClient):
pass
class TrezorClientDebug(ProtocolMixin, TextUIMixin, DebugWireMixin, BaseClient):
class TrezorClientVerbose(ProtocolMixin, TextUIMixin, VerboseWireMixin, BaseClient):
pass
class TrezorDebugClient(ProtocolMixin, DebugLinkMixin, DebugWireMixin, BaseClient):
class TrezorClientDebugLink(ProtocolMixin, DebugLinkMixin, VerboseWireMixin, BaseClient):
pass

33
trezorlib/coins.py Normal file
View File

@ -0,0 +1,33 @@
from .tx_api import TxApiBitcoin, TxApiTestnet, TxApiLitecoin, TxApiZcash, TxApiDash, TxApiBcash, TxApiDecredTestnet, TxApiDogecoin, TxApiMonacoin, TxApiBitcoinGold, TxApiBitcoinPrivate
coins_slip44 = {
'Bitcoin': 0,
'Testnet': 1,
'Decred Testnet': 1,
'Litecoin': 2,
'Dogecoin': 3,
'Dash': 5,
'Namecoin': 7,
'Monacoin': 22,
'Decred': 42,
'Ether': 60,
'EtherClassic': 61,
'Zcash': 133,
'Bcash': 145,
'Bitcoin Gold': 156,
'Bitcoin Private': 183,
}
coins_txapi = {
'Bitcoin': TxApiBitcoin,
'Testnet': TxApiTestnet,
'Litecoin': TxApiLitecoin,
'Dash': TxApiDash,
'Zcash': TxApiZcash,
'Bcash': TxApiBcash,
'Decred Testnet': TxApiDecredTestnet,
'Dogecoin': TxApiDogecoin,
'Monacoin': TxApiMonacoin,
'Bitcoin Gold': TxApiBitcoinGold,
'Bitcoin Private': TxApiBitcoinPrivate,
}

View File

@ -18,7 +18,7 @@
from __future__ import print_function
from . import messages_pb2 as proto
from . import messages as proto
def pin_info(pin):
@ -43,14 +43,13 @@ class DebugLink(object):
def close(self):
self.transport.session_end()
self.transport.close()
def _call(self, msg, nowait=False):
print("DEBUGLINK SEND", pprint(msg))
self.transport.write(msg)
if nowait:
return
ret = self.transport.read_blocking()
ret = self.transport.read()
print("DEBUGLINK RECV", pprint(ret))
return ret

39
trezorlib/device.py Normal file
View File

@ -0,0 +1,39 @@
# This file is part of the TREZOR project.
#
# Copyright (C) 2012-2017 Marek Palatinus <slush@satoshilabs.com>
# Copyright (C) 2012-2017 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/>.
import warnings
from .transport import enumerate_devices, get_transport
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):
warnings.warn('TrezorDevice is deprecated.', DeprecationWarning)
return enumerate_devices()
@classmethod
def find_by_path(cls, path):
warnings.warn('TrezorDevice is deprecated.', DeprecationWarning)
return get_transport(path, prefix_search=False)

89
trezorlib/ed25519cosi.py Normal file
View File

@ -0,0 +1,89 @@
import sys
from functools import reduce
import binascii
from . import ed25519raw
def combine_keys(pks):
P = [ed25519raw.decodepoint(pk) for pk in pks]
combine = reduce(ed25519raw.edwards, P)
return ed25519raw.encodepoint(combine)
def combine_sig(R, sigs):
S = [ed25519raw.decodeint(si) for si in sigs]
s = sum(S) % ed25519raw.l
sig = R + ed25519raw.encodeint(s)
return sig
def get_nonce(sk, data, ctr):
h = ed25519raw.H(sk)
b = ed25519raw.b
r = ed25519raw.Hint(bytes([h[i] for i in range(b >> 3, b >> 2)]) + data + binascii.unhexlify('%08x' % ctr))
R = ed25519raw.scalarmult(ed25519raw.B, r)
return r, ed25519raw.encodepoint(R)
def self_test(digest):
def to_hex(by):
return binascii.hexlify(by).decode()
N = 3
keyset = [0, 2]
digest = binascii.unhexlify(digest)
print('Digest: %s' % to_hex(digest))
sks = []
pks = []
nonces = []
commits = []
sigs = []
for i in range(0, N):
print('----- Key %d ------' % (i + 1))
seckey = (chr(0x41 + i) * 32).encode()
pubkey = ed25519raw.publickey(seckey)
print('Secret Key: %s' % to_hex(seckey))
print('Public Key: %s' % to_hex(pubkey))
sks.append(seckey)
pks.append(pubkey)
ctr = 0
r, R = get_nonce(seckey, digest, ctr)
print('Local nonce: %s' % to_hex(ed25519raw.encodeint(r)))
print('Local commit: %s' % to_hex(R))
nonces.append(r)
commits.append(R)
global_pk = combine_keys([pks[i] for i in keyset])
global_R = combine_keys([commits[i] for i in keyset])
print('-----------------')
print('Global pubkey: %s' % to_hex(global_pk))
print('Global commit: %s' % to_hex(global_R))
print('-----------------')
for i in range(0, N):
seckey = sks[i]
pubkey = pks[i]
r = nonces[i]
R = commits[i]
h = ed25519raw.H(seckey)
b = ed25519raw.b
a = 2**(b - 2) + sum(2**i * ed25519raw.bit(h, i) for i in range(3, b - 2))
S = (r + ed25519raw.Hint(global_R + global_pk + digest) * a) % ed25519raw.l
print('Local sig %d: %s' % (i + 1, to_hex(ed25519raw.encodeint(S))))
sigs.append(ed25519raw.encodeint(S))
print('-----------------')
sig = combine_sig(global_R, [sigs[i] for i in keyset])
print('Global sig: %s' % to_hex(sig))
ed25519raw.checkvalid(sig, digest, global_pk)
print('Valid Signature!')
if __name__ == '__main__':
if len(sys.argv) > 1:
self_test(digest=sys.argv[1])
else:
self_test(digest='4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b')

138
trezorlib/ed25519raw.py Normal file
View File

@ -0,0 +1,138 @@
# orignal version downloaded from https://ed25519.cr.yp.to/python/ed25519.py
# modified for Python 3 by Jochen Hoenicke <hoenicke@gmail.com>
import sys
import hashlib
b = 256
q = 2 ** 255 - 19
l = 2 ** 252 + 27742317777372353535851937790883648493
def H(m):
return hashlib.sha512(m).digest()
def expmod(b, e, m):
if e < 0:
raise ValueError('negative exponent')
if e == 0:
return 1
t = expmod(b, e >> 1, m) ** 2 % m
if e & 1:
t = (t * b) % m
return t
def inv(x):
return expmod(x, q - 2, q)
d = -121665 * inv(121666)
I = expmod(2, (q - 1) >> 2, q)
def xrecover(y):
xx = (y * y - 1) * inv(d * y * y + 1)
x = expmod(xx, (q + 3) >> 3, q)
if (x * x - xx) % q != 0:
x = (x * I) % q
if x % 2 != 0:
x = q - x
return x
By = 4 * inv(5)
Bx = xrecover(By)
B = [Bx % q, By % q]
def edwards(P, Q):
x1 = P[0]
y1 = P[1]
x2 = Q[0]
y2 = Q[1]
x3 = (x1 * y2 + x2 * y1) * inv(1 + d * x1 * x2 * y1 * y2)
y3 = (y1 * y2 + x1 * x2) * inv(1 - d * x1 * x2 * y1 * y2)
return [x3 % q, y3 % q]
def scalarmult(P, e):
if e == 0:
return [0, 1]
Q = scalarmult(P, e >> 1)
Q = edwards(Q, Q)
if e & 1:
Q = edwards(Q, P)
return Q
def encodeint(y):
bits = [(y >> i) & 1 for i in range(b)]
return bytes([sum([bits[i * 8 + j] << j for j in range(8)]) for i in range(b >> 3)])
def encodepoint(P):
x = P[0]
y = P[1]
bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1]
return bytes([sum([bits[i * 8 + j] << j for j in range(8)]) for i in range(b >> 3)])
def bit(h, i):
return (h[i >> 3] >> (i & 7)) & 1
def publickey(sk):
h = H(sk)
a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2))
A = scalarmult(B, a)
return encodepoint(A)
def Hint(m):
h = H(m)
return sum(2 ** i * bit(h, i) for i in range(2 * b))
def signature(m, sk, pk):
h = H(sk)
a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2))
r = Hint(bytes([h[i] for i in range(b >> 3, b >> 2)]) + m)
R = scalarmult(B, r)
S = (r + Hint(encodepoint(R) + pk + m) * a) % l
return encodepoint(R) + encodeint(S)
def isoncurve(P):
x = P[0]
y = P[1]
return (-x * x + y * y - 1 - d * x * x * y * y) % q == 0
def decodeint(s):
return sum(2 ** i * bit(s, i) for i in range(0, b))
def decodepoint(s):
y = sum(2 ** i * bit(s, i) for i in range(0, b - 1))
x = xrecover(y)
if x & 1 != bit(s, b - 1):
x = q - x
P = [x, y]
if not isoncurve(P):
raise ValueError('decoding point that is not on curve')
return P
def checkvalid(s, m, pk):
if len(s) != b >> 2:
raise ValueError('signature length is wrong')
if len(pk) != b >> 3:
raise ValueError('public-key length is wrong')
R = decodepoint(s[0:b >> 3])
A = decodepoint(pk)
S = decodeint(s[b >> 3:b >> 2])
h = Hint(encodepoint(R) + pk + m)
if scalarmult(B, S) != edwards(R, scalarmult(A, h)):
raise ValueError('signature does not pass verification')

View File

@ -2,6 +2,7 @@
#
# 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
@ -16,19 +17,36 @@
# 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 . import messages_pb2 as proto
from . import messages
from . import protobuf
map_type_to_class = {}
map_class_to_type = {}
def build_map():
for msg_type, i in proto.MessageType.items():
msg_name = msg_type.replace('MessageType_', '')
msg_class = getattr(proto, msg_name)
for msg_name in dir(messages.MessageType):
if msg_name.startswith('__'):
continue
map_type_to_class[i] = msg_class
map_class_to_type[msg_class] = i
try:
msg_class = getattr(messages, msg_name)
except AttributeError:
raise ValueError("Implementation of protobuf message '%s' is missing" % msg_name)
if msg_class.MESSAGE_WIRE_TYPE != getattr(messages.MessageType, msg_name):
raise ValueError("Inconsistent wire type and MessageType record for '%s'" % msg_class)
register_message(msg_class)
def register_message(msg_class):
if msg_class.MESSAGE_WIRE_TYPE in map_type_to_class:
raise Exception("Message for wire type %s is already registered by %s" %
(msg_class.MESSAGE_WIRE_TYPE, get_class(msg_class.MESSAGE_WIRE_TYPE)))
map_class_to_type[msg_class] = msg_class.MESSAGE_WIRE_TYPE
map_type_to_class[msg_class.MESSAGE_WIRE_TYPE] = msg_class
def get_type(msg):
@ -39,17 +57,4 @@ def get_class(t):
return map_type_to_class[t]
def check_missing():
from google.protobuf import reflection
types = [getattr(proto, item) for item in dir(proto)
if issubclass(getattr(proto, item).__class__, reflection.GeneratedProtocolMessageType)]
missing = list(set(types) - set(map_type_to_class.values()))
if len(missing):
raise Exception("Following protobuf messages are not defined in mapping: %s" % missing)
build_map()
check_missing()

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class Address(p.MessageType):
FIELDS = {
1: ('address', p.UnicodeType, 0), # required
}
MESSAGE_WIRE_TYPE = 30

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class ApplyFlags(p.MessageType):
FIELDS = {
1: ('flags', p.UVarintType, 0),
}
MESSAGE_WIRE_TYPE = 28

View File

@ -0,0 +1,13 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class ApplySettings(p.MessageType):
FIELDS = {
1: ('language', p.UnicodeType, 0),
2: ('label', p.UnicodeType, 0),
3: ('use_passphrase', p.BoolType, 0),
4: ('homescreen', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 25

View File

@ -0,0 +1,7 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class BackupDevice(p.MessageType):
MESSAGE_WIRE_TYPE = 34

View File

@ -0,0 +1,7 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class ButtonAck(p.MessageType):
MESSAGE_WIRE_TYPE = 27

View File

@ -0,0 +1,11 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class ButtonRequest(p.MessageType):
FIELDS = {
1: ('code', p.UVarintType, 0),
2: ('data', p.UnicodeType, 0),
}
MESSAGE_WIRE_TYPE = 26

View File

@ -0,0 +1,15 @@
# Automatically generated by pb2py
Other = 1
FeeOverThreshold = 2
ConfirmOutput = 3
ResetDevice = 4
ConfirmWord = 5
WipeDevice = 6
ProtectCall = 7
SignTx = 8
FirmwareCheck = 9
Address = 10
PublicKey = 11
MnemonicWordCount = 12
MnemonicInput = 13
PassphraseType = 14

View File

@ -0,0 +1,7 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class Cancel(p.MessageType):
MESSAGE_WIRE_TYPE = 20

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class ChangePin(p.MessageType):
FIELDS = {
1: ('remove', p.BoolType, 0),
}
MESSAGE_WIRE_TYPE = 4

View File

@ -0,0 +1,16 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class CipherKeyValue(p.MessageType):
FIELDS = {
1: ('address_n', p.UVarintType, p.FLAG_REPEATED),
2: ('key', p.UnicodeType, 0),
3: ('value', p.BytesType, 0),
4: ('encrypt', p.BoolType, 0),
5: ('ask_on_encrypt', p.BoolType, 0),
6: ('ask_on_decrypt', p.BoolType, 0),
7: ('iv', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 23

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class CipheredKeyValue(p.MessageType):
FIELDS = {
1: ('value', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 48

View File

@ -0,0 +1,7 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class ClearSession(p.MessageType):
MESSAGE_WIRE_TYPE = 24

View File

@ -0,0 +1,19 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class CoinType(p.MessageType):
FIELDS = {
1: ('coin_name', p.UnicodeType, 0),
2: ('coin_shortcut', p.UnicodeType, 0),
3: ('address_type', p.UVarintType, 0), # default=0
4: ('maxfee_kb', p.UVarintType, 0),
5: ('address_type_p2sh', p.UVarintType, 0), # default=5
8: ('signed_message_header', p.UnicodeType, 0),
9: ('xpub_magic', p.UVarintType, 0), # default=76067358
10: ('xprv_magic', p.UVarintType, 0), # default=76066276
11: ('segwit', p.BoolType, 0),
12: ('forkid', p.UVarintType, 0),
13: ('force_bip143', p.BoolType, 0),
}

View File

@ -0,0 +1,11 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class CosiCommit(p.MessageType):
FIELDS = {
1: ('address_n', p.UVarintType, p.FLAG_REPEATED),
2: ('data', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 71

View File

@ -0,0 +1,11 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class CosiCommitment(p.MessageType):
FIELDS = {
1: ('commitment', p.BytesType, 0),
2: ('pubkey', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 72

View File

@ -0,0 +1,13 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class CosiSign(p.MessageType):
FIELDS = {
1: ('address_n', p.UVarintType, p.FLAG_REPEATED),
2: ('data', p.BytesType, 0),
3: ('global_commitment', p.BytesType, 0),
4: ('global_pubkey', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 73

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class CosiSignature(p.MessageType):
FIELDS = {
1: ('signature', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 74

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class DebugLinkDecision(p.MessageType):
FIELDS = {
1: ('yes_no', p.BoolType, 0), # required
}
MESSAGE_WIRE_TYPE = 100

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class DebugLinkFlashErase(p.MessageType):
FIELDS = {
1: ('sector', p.UVarintType, 0),
}
MESSAGE_WIRE_TYPE = 113

View File

@ -0,0 +1,7 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class DebugLinkGetState(p.MessageType):
MESSAGE_WIRE_TYPE = 101

View File

@ -0,0 +1,12 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class DebugLinkLog(p.MessageType):
FIELDS = {
1: ('level', p.UVarintType, 0),
2: ('bucket', p.UnicodeType, 0),
3: ('text', p.UnicodeType, 0),
}
MESSAGE_WIRE_TYPE = 104

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class DebugLinkMemory(p.MessageType):
FIELDS = {
1: ('memory', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 111

View File

@ -0,0 +1,11 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class DebugLinkMemoryRead(p.MessageType):
FIELDS = {
1: ('address', p.UVarintType, 0),
2: ('length', p.UVarintType, 0),
}
MESSAGE_WIRE_TYPE = 110

View File

@ -0,0 +1,12 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class DebugLinkMemoryWrite(p.MessageType):
FIELDS = {
1: ('address', p.UVarintType, 0),
2: ('memory', p.BytesType, 0),
3: ('flash', p.BoolType, 0),
}
MESSAGE_WIRE_TYPE = 112

View File

@ -0,0 +1,20 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
from .HDNodeType import HDNodeType
class DebugLinkState(p.MessageType):
FIELDS = {
1: ('layout', p.BytesType, 0),
2: ('pin', p.UnicodeType, 0),
3: ('matrix', p.UnicodeType, 0),
4: ('mnemonic', p.UnicodeType, 0),
5: ('node', HDNodeType, 0),
6: ('passphrase_protection', p.BoolType, 0),
7: ('reset_word', p.UnicodeType, 0),
8: ('reset_entropy', p.BytesType, 0),
9: ('recovery_fake_word', p.UnicodeType, 0),
10: ('recovery_word_pos', p.UVarintType, 0),
}
MESSAGE_WIRE_TYPE = 102

View File

@ -0,0 +1,7 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class DebugLinkStop(p.MessageType):
MESSAGE_WIRE_TYPE = 103

View File

@ -0,0 +1,13 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class DecryptMessage(p.MessageType):
FIELDS = {
1: ('address_n', p.UVarintType, p.FLAG_REPEATED),
2: ('nonce', p.BytesType, 0),
3: ('message', p.BytesType, 0),
4: ('hmac', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 51

View File

@ -0,0 +1,11 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class DecryptedMessage(p.MessageType):
FIELDS = {
1: ('message', p.BytesType, 0),
2: ('address', p.UnicodeType, 0),
}
MESSAGE_WIRE_TYPE = 52

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class ECDHSessionKey(p.MessageType):
FIELDS = {
1: ('session_key', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 62

View File

@ -0,0 +1,14 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class EncryptMessage(p.MessageType):
FIELDS = {
1: ('pubkey', p.BytesType, 0),
2: ('message', p.BytesType, 0),
3: ('display_only', p.BoolType, 0),
4: ('address_n', p.UVarintType, p.FLAG_REPEATED),
5: ('coin_name', p.UnicodeType, 0), # default='Bitcoin'
}
MESSAGE_WIRE_TYPE = 49

View File

@ -0,0 +1,12 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class EncryptedMessage(p.MessageType):
FIELDS = {
1: ('nonce', p.BytesType, 0),
2: ('message', p.BytesType, 0),
3: ('hmac', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 50

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class Entropy(p.MessageType):
FIELDS = {
1: ('entropy', p.BytesType, 0), # required
}
MESSAGE_WIRE_TYPE = 10

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class EntropyAck(p.MessageType):
FIELDS = {
1: ('entropy', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 36

View File

@ -0,0 +1,7 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class EntropyRequest(p.MessageType):
MESSAGE_WIRE_TYPE = 35

View File

@ -0,0 +1,12 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class EstimateTxSize(p.MessageType):
FIELDS = {
1: ('outputs_count', p.UVarintType, 0), # required
2: ('inputs_count', p.UVarintType, 0), # required
3: ('coin_name', p.UnicodeType, 0), # default='Bitcoin'
}
MESSAGE_WIRE_TYPE = 43

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class EthereumAddress(p.MessageType):
FIELDS = {
1: ('address', p.BytesType, 0), # required
}
MESSAGE_WIRE_TYPE = 57

View File

@ -0,0 +1,11 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class EthereumGetAddress(p.MessageType):
FIELDS = {
1: ('address_n', p.UVarintType, p.FLAG_REPEATED),
2: ('show_display', p.BoolType, 0),
}
MESSAGE_WIRE_TYPE = 56

View File

@ -0,0 +1,11 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class EthereumMessageSignature(p.MessageType):
FIELDS = {
1: ('address', p.BytesType, 0),
2: ('signature', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 66

View File

@ -0,0 +1,11 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class EthereumSignMessage(p.MessageType):
FIELDS = {
1: ('address_n', p.UVarintType, p.FLAG_REPEATED),
2: ('message', p.BytesType, 0), # required
}
MESSAGE_WIRE_TYPE = 64

View File

@ -0,0 +1,18 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class EthereumSignTx(p.MessageType):
FIELDS = {
1: ('address_n', p.UVarintType, p.FLAG_REPEATED),
2: ('nonce', p.BytesType, 0),
3: ('gas_price', p.BytesType, 0),
4: ('gas_limit', p.BytesType, 0),
5: ('to', p.BytesType, 0),
6: ('value', p.BytesType, 0),
7: ('data_initial_chunk', p.BytesType, 0),
8: ('data_length', p.UVarintType, 0),
9: ('chain_id', p.UVarintType, 0),
}
MESSAGE_WIRE_TYPE = 58

View File

@ -0,0 +1,10 @@
# Automatically generated by pb2py
from __future__ import absolute_import
from .. import protobuf as p
class EthereumTxAck(p.MessageType):
FIELDS = {
1: ('data_chunk', p.BytesType, 0),
}
MESSAGE_WIRE_TYPE = 60

Some files were not shown because too many files have changed in this diff Show More