From 72ab93bd87bcca36c2db65c68b8dcf87562e34ad Mon Sep 17 00:00:00 2001 From: mruddy Date: Sun, 26 Feb 2017 09:32:23 -0500 Subject: [PATCH] add a tx signing tool that handles bitcoin, testnet, and litecoin --- tools/tx_sign_tool.py | 97 +++++++++++++++++++++++++++++++++++++++++++ trezorlib/tx_api.py | 35 +++++++++++++++- 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100755 tools/tx_sign_tool.py diff --git a/tools/tx_sign_tool.py b/tools/tx_sign_tool.py new file mode 100755 index 000000000..c0618f064 --- /dev/null +++ b/tools/tx_sign_tool.py @@ -0,0 +1,97 @@ +#!/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 * +from trezorlib import types_pb2 as types + +def main(): + print('Welcome to the user-unfriendly transaction signing tool') + print('USE AT YOUR OWN RISK') + + coin = raw_input('Which coin {Bitcoin, Testnet, Litecoin}? ').strip() + + if coin not in {'Bitcoin', 'Testnet', 'Litecoin'}: + print('not supported') + exit(1) + + # 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) + + txapi_lookup = { + 'Bitcoin': TxApiBitcoin, + 'Testnet': TxApiTestnet, + 'Litecoin': TxApiLitecoin + } + + client.set_tx_api(txapi_lookup[coin]) + + inputs = [] + + while True: + prev_in_hash = raw_input('Previous input hash (enter nothing to move on): ').strip() + if prev_in_hash == '': + break + prev_in_vout = raw_input(' Previous input index: ').strip() + addrn = raw_input(" Node to sign with (e.g.- " + coin + "/0'/0/0): ").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: + out_addr = raw_input('Pay to address (enter nothing to move on): ').strip() + if out_addr == '': + break + out_amount = raw_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) + + 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/ + + client.close() + +if __name__ == '__main__': + main() diff --git a/trezorlib/tx_api.py b/trezorlib/tx_api.py index b1dd2b5de..6f406b48f 100644 --- a/trezorlib/tx_api.py +++ b/trezorlib/tx_api.py @@ -41,7 +41,7 @@ class TxApi(object): except: pass try: - r = requests.get('%s/%s/%s' % (self.url, resource, resourceid), headers={'User-agent': 'Mozilla/5.0'}) + r = requests.get('%s%s/%s' % (self.url, resource, resourceid), headers={'User-agent': 'Mozilla/5.0'}) j = r.json() except: raise Exception('URL error: %s' % url) @@ -140,7 +140,40 @@ class TxApiSmartbit(TxApi): return t +class TxApiBlockCypher(TxApi): + + def get_tx(self, txhash): + + data = self.fetch_json(self.url, 'txs', txhash) + + t = proto_types.TransactionType() + t.version = data['ver'] + t.lock_time = data['lock_time'] + + for vin in data['inputs']: + i = t.inputs.add() + if 'prev_hash' not in vin: + i.prev_hash = b"\0"*32 + i.prev_index = 0xffffffff # signed int -1 + i.script_sig = binascii.unhexlify(vin['script']) + i.sequence = vin['sequence'] + else: + i.prev_hash = binascii.unhexlify(vin['prev_hash']) + i.prev_index = vin['output_index'] + i.script_sig = binascii.unhexlify(vin['script']) + i.sequence = vin['sequence'] + + for vout in data['outputs']: + o = t.bin_outputs.add() + o.amount = int(str(vout['value']), 10) + o.script_pubkey = binascii.unhexlify(vout['script']) + + 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/') 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) +