From 23d75bfc107285e9d75fa1d443b4ee1c8b8a07c3 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sat, 16 Sep 2017 00:05:47 +0900 Subject: [PATCH] trezorctl: sign_tx command based on tx_sign_tool by mruddy --- setup.py | 1 + tools/tx_sign_tool.py | 114 ------------------------------------------ trezorctl | 71 ++++++++++++++++++++++++-- trezorlib/client.py | 17 ++----- trezorlib/coins.py | 24 +++++++++ trezorlib/tx_api.py | 11 ++-- 6 files changed, 103 insertions(+), 135 deletions(-) delete mode 100755 tools/tx_sign_tool.py create mode 100644 trezorlib/coins.py diff --git a/setup.py b/setup.py index 521a43e..05583fe 100755 --- a/setup.py +++ b/setup.py @@ -26,6 +26,7 @@ setup( py_modules=[ 'trezorlib.ckd_public', 'trezorlib.client', + 'trezorlib.coins', 'trezorlib.debuglink', 'trezorlib.ed25519cosi', 'trezorlib.ed25519raw', diff --git a/tools/tx_sign_tool.py b/tools/tx_sign_tool.py deleted file mode 100755 index e476e62..0000000 --- a/tools/tx_sign_tool.py +++ /dev/null @@ -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 . - -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 = 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() diff --git a/trezorctl b/trezorctl index 15072fe..020a70d 100755 --- a/trezorctl +++ b/trezorctl @@ -2,9 +2,10 @@ # This file is part of the TREZOR project. # -# Copyright (C) 2012-2016 Marek Palatinus -# Copyright (C) 2012-2016 Pavol Rusnak -# Copyright (C) 2016 Jochen Hoenicke +# Copyright (C) 2012-2017 Marek Palatinus +# Copyright (C) 2012-2017 Pavol Rusnak +# Copyright (C) 2016-2017 Jochen Hoenicke +# 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 @@ -27,6 +28,7 @@ import json from trezorlib.client import TrezorClient, TrezorClientVerbose, CallException import trezorlib.types_pb2 as types +from trezorlib.coins import coins_txapi def get_transport_class_by_name(name): @@ -431,6 +433,69 @@ def get_public_node(connect, coin, address, curve, show_display): } +# +# Signing options +# + +@cli.command(help='Sign transaction.') +@click.option('-c', '--coin', default='Bitcoin') +# @click.option('-n', '--address', required=True, help="BIP-32 path, e.g. m/44'/0'/0'/0/0") +# @click.option('-t', '--script-type', type=click.Choice(['address', 'segwit', 'p2shsegwit']), default='address') +# @click.option('-o', '--output', required=True, help='Transaction output') +# @click.option('-f', '--fee', required=True, help='Transaction fee (sat/B)') +@click.pass_obj +def sign_tx(connect, coin): + client = connect() + try: + txapi = coins_txapi[coin] + except: + raise Exception('Coin "%s" is not supported' % coin) + client.set_tx_api(txapi) + + try: + input = raw_input + except: + pass + + inputs = [] + while True: + click.echo() + prev = input('Input (prevhash:previndex, empty to move on): ').strip() + if prev == '': + break + prev_in_hash, prev_in_vout = prev.split(':') + 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: + click.echo() + 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() + + click.echo() + click.echo('Signed Transaction:') + click.echo(binascii.hexlify(serialized_tx)) + click.echo() + click.echo('Use the following form to broadcast it to the network:') + click.echo(txapi.url.replace('/api/', '/tx/send')) + + # # Message functions # diff --git a/trezorlib/client.py b/trezorlib/client.py index 9da68ee..cabf946 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -34,6 +34,7 @@ from . import tools # from . import mapping from . import messages_pb2 as proto from . import types_pb2 as types +from .coins import coins_slip44 from .debuglink import DebugLink # Python2 vs Python3 @@ -481,20 +482,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: diff --git a/trezorlib/coins.py b/trezorlib/coins.py new file mode 100644 index 0000000..6d777ec --- /dev/null +++ b/trezorlib/coins.py @@ -0,0 +1,24 @@ +from .tx_api import TxApiBitcoin, TxApiTestnet, TxApiLitecoin, TxApiZcash, TxApiDash, TxApiBcash + +coins_slip44 = { + 'Bitcoin': 0, + 'Testnet': 1, + 'Litecoin': 2, + 'Dogecoin': 3, + 'Dash': 5, + 'Namecoin': 7, + 'Decred': 42, + 'Ether': 60, + 'EtherClassic': 61, + 'Zcash': 133, + 'Bcash': 145, +} + +coins_txapi = { + 'Bitcoin': TxApiBitcoin, + 'Testnet': TxApiTestnet, + 'Litecoin': TxApiLitecoin, + 'Dash': TxApiDash, + 'Zcash': TxApiZcash, + 'Bcash': TxApiBcash, +} diff --git a/trezorlib/tx_api.py b/trezorlib/tx_api.py index 6acb208..66e7b68 100644 --- a/trezorlib/tx_api.py +++ b/trezorlib/tx_api.py @@ -173,8 +173,11 @@ class TxApiBlockCypher(TxApi): return t -TxApiBitcoin = TxApiInsight(network='insight_bitcoin', url='https://insight.bitpay.com/api/') -TxApiTestnet = TxApiInsight(network='insight_testnet', url='https://test-insight.bitpay.com/api/') -TxApiLitecoin = TxApiBlockCypher(network='blockcypher_litecoin', url='https://api.blockcypher.com/v1/ltc/main/') +TxApiBitcoin = TxApiInsight(network='insight_bitcoin', url='https://btc-bitcore1.trezor.io/api/') +TxApiTestnet = TxApiInsight(network='insight_testnet', url='https://testnet-bitcore3.trezor.io/api/') +TxApiLitecoin = TxApiInsight(network='insight_litecoin', url='https://ltc-bitcore1.trezor.io/api/') +TxApiDash = TxApiInsight(network='insight_dash', url='https://dash-bitcore1.trezor.io/api/') +TxApiZcash = TxApiInsight(network='insight_zcash', url='https://zec-bitcore1.trezor.io/api/', zcash=True) +TxApiBcash = TxApiInsight(network='insight_zcash', url='https://bch-bitcore2.trezor.io/api/') + TxApiSegnet = TxApiSmartbit(network='smartbit_segnet', url='https://segnet-api.smartbit.com.au/v1/blockchain/') -TxApiZcashTestnet = TxApiInsight(network='insight_zcashtestnet', url='https://explorer.testnet.z.cash/api/', zcash=True)