trezorctl: sign_tx command based on tx_sign_tool by mruddy

This commit is contained in:
Pavol Rusnak 2017-09-16 00:05:47 +09:00
parent 54426761c6
commit 23d75bfc10
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D
6 changed files with 103 additions and 135 deletions

View File

@ -26,6 +26,7 @@ setup(
py_modules=[
'trezorlib.ckd_public',
'trezorlib.client',
'trezorlib.coins',
'trezorlib.debuglink',
'trezorlib.ed25519cosi',
'trezorlib.ed25519raw',

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 = 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

@ -2,9 +2,10 @@
# This file is part of the TREZOR project.
#
# Copyright (C) 2012-2016 Marek Palatinus <slush@satoshilabs.com>
# Copyright (C) 2012-2016 Pavol Rusnak <stick@satoshilabs.com>
# Copyright (C) 2016 Jochen Hoenicke <hoenicke@gmail.com>
# Copyright (C) 2012-2017 Marek Palatinus <slush@satoshilabs.com>
# Copyright (C) 2012-2017 Pavol Rusnak <stick@satoshilabs.com>
# Copyright (C) 2016-2017 Jochen Hoenicke <hoenicke@gmail.com>
# 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
#

View File

@ -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:

24
trezorlib/coins.py Normal file
View File

@ -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,
}

View File

@ -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)