From 5a677c3782cbbeaffa1672424f738b890d725392 Mon Sep 17 00:00:00 2001 From: "D.Matskevych" Date: Wed, 25 Jul 2018 12:38:35 +0300 Subject: [PATCH] Added Zencash supporting --- trezorctl | 13 ++++++++++++ trezorlib/coins.py | 3 ++- trezorlib/tests/unit_tests/test_tx_api.py | 12 ++++++++++++ trezorlib/tx_api.py | 24 +++++++++++++++++++++-- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/trezorctl b/trezorctl index 0a797d490..aaa4607ad 100755 --- a/trezorctl +++ b/trezorctl @@ -576,6 +576,10 @@ def sign_tx(connect, coin): sequence = click.prompt('Sequence Number to use (RBF opt-in enabled by default)', type=int, default=0xfffffffd) script_type = click.prompt('Input type', type=CHOICE_INPUT_SCRIPT_TYPE, default=default_script_type(address_n)) script_type = script_type if isinstance(script_type, int) else CHOICE_INPUT_SCRIPT_TYPE.typemap[script_type] + if txapi.bip115: + prev_output = txapi.get_tx(binascii.hexlify(prev_hash).decode("utf-8")).bin_outputs[prev_index] + prev_blockhash = prev_output.block_hash + prev_blockheight = prev_output.block_height inputs.append(proto.TxInputType( address_n=address_n, prev_hash=prev_hash, @@ -583,8 +587,15 @@ def sign_tx(connect, coin): amount=amount, script_type=script_type, sequence=sequence, + prev_block_hash_bip115=prev_blockhash, + prev_block_height_bip115=prev_blockheight, )) + if txapi.bip115: + current_block_height = txapi.current_height() + block_height = current_block_height - 300 # Zencash recommendation for the better protection + block_hash = txapi.get_block_hash(block_height) + outputs = [] while True: click.echo() @@ -604,6 +615,8 @@ def sign_tx(connect, coin): address=address, amount=amount, script_type=script_type, + block_hash_bip115=block_hash[::-1], # Blockhash passed in reverse order + block_height_bip115=block_height )) tx_version = click.prompt('Transaction version', type=int, default=2) diff --git a/trezorlib/coins.py b/trezorlib/coins.py index c4818c611..741b9bcf4 100644 --- a/trezorlib/coins.py +++ b/trezorlib/coins.py @@ -39,8 +39,9 @@ def _insight_for_coin(coin): if not url: return None zcash = coin['coin_name'].lower().startswith('zcash') + bip115 = coin['bip115'] network = 'insight_{}'.format(coin['coin_name'].lower().replace(' ', '_')) - return TxApiInsight(network=network, url=url, zcash=zcash) + return TxApiInsight(network=network, url=url, zcash=zcash, bip115=bip115) # exported variables diff --git a/trezorlib/tests/unit_tests/test_tx_api.py b/trezorlib/tests/unit_tests/test_tx_api.py index f67caea44..e914b8281 100644 --- a/trezorlib/tests/unit_tests/test_tx_api.py +++ b/trezorlib/tests/unit_tests/test_tx_api.py @@ -18,9 +18,11 @@ import os from trezorlib import coins from trezorlib import tx_api +import binascii TxApiBitcoin = coins.tx_api['Bitcoin'] TxApiTestnet = tx_api.TxApiInsight("insight_testnet") +TxApiZencash = coins.tx_api['Zencash'] tests_dir = os.path.dirname(os.path.abspath(__file__)) @@ -42,3 +44,13 @@ def test_tx_api_gettx(): TxApiTestnet.get_tx('6f90f3c7cbec2258b0971056ef3fe34128dbde30daa9c0639a898f9977299d54') TxApiTestnet.get_tx('d6da21677d7cca5f42fbc7631d062c9ae918a0254f7c6c22de8e8cb7fd5b8236') + + +def test_tx_api_current_block(): + height = TxApiZencash.current_height() + assert height > 347041 + + +def test_tx_api_get_block_hash(): + hash = TxApiZencash.get_block_hash(110000) + assert hash == binascii.unhexlify('000000003f5d6ba1385c6cd2d4f836dfc5adf7f98834309ad67e26faef462454') diff --git a/trezorlib/tx_api.py b/trezorlib/tx_api.py index 28bf37e43..f72ad63ab 100644 --- a/trezorlib/tx_api.py +++ b/trezorlib/tx_api.py @@ -33,6 +33,18 @@ class TxApi(object): url = '%s%s/%s' % (self.url, resource, resourceid) return url + def current_height(self): + r = requests.get(self.url + '/status?q=getBlockCount') + j = r.json(parse_float=str) + block_height = j['info']['blocks'] + return block_height + + def get_block_hash(self, block_number): + r = requests.get(self.url + '/block-index/' + str(block_number)) + j = r.json(parse_float=str) + block_hash = binascii.unhexlify(j['blockHash']) + return block_hash + def fetch_json(self, resource, resourceid): global cache_dir if cache_dir: @@ -65,11 +77,13 @@ class TxApi(object): class TxApiInsight(TxApi): - def __init__(self, network, url=None, zcash=None): + def __init__(self, network, url=None, zcash=None, bip115=False): super().__init__(network, url) self.zcash = zcash + self.bip115 = bip115 if url: - self.pushtx_url = url.replace('/api/', '/tx/send') + prefix, suffix = url.rsplit('/', maxsplit=1) + self.pushtx_url = prefix + '/tx/send' def get_tx(self, txhash): @@ -97,6 +111,12 @@ class TxApiInsight(TxApi): o = t._add_bin_outputs() o.amount = int(Decimal(vout['value']) * 100000000) o.script_pubkey = binascii.unhexlify(vout['scriptPubKey']['hex']) + if self.bip115 and o.script_pubkey[-1] == 0xb4: + # Verify if coin implements replay protection bip115 and script includes checkblockatheight opcode. 0xb4 - is op_code (OP_CHECKBLOCKATHEIGHT) + # <32-byte block hash> <3-byte block height> + tail = o.script_pubkey[-38:] + o.block_hash = tail[1:33] # <32-byte block hash> + o.block_height = int.from_bytes(tail[34:37], byteorder='little') # <3-byte block height> if self.zcash: t.overwintered = data.get('fOverwintered', False)