From 19a0f4cc7bd19c417580302da475bb63ec4a3b1c Mon Sep 17 00:00:00 2001 From: matejcik Date: Tue, 3 Jul 2018 17:26:22 +0200 Subject: [PATCH] tx_api: support blockbook, allow url-less operation Bitcoin Testnet backends were removed. This broke our tests that work purely on local data but nevertheless need to know backend URLs. Because the design is less than ideal. Here, we do a little cleanup: * blockbook URLs are respected * Dogecoin is now supported by our blockbook backend so we don't need a special tx-api implementation * it's possible to instantiate a TxApi that is backed by the cache, without a known URL * relevant tests now make use of this feature --- trezorlib/coins.py | 11 ++--- .../tests/device_tests/test_msg_signtx.py | 4 +- .../device_tests/test_msg_signtx_segwit.py | 4 +- .../test_msg_signtx_segwit_native.py | 4 +- .../device_tests/test_multisig_change.py | 6 ++- trezorlib/tests/unit_tests/test_tx_api.py | 2 +- trezorlib/tx_api.py | 48 ++++--------------- 7 files changed, 23 insertions(+), 56 deletions(-) diff --git a/trezorlib/coins.py b/trezorlib/coins.py index 2d4ec2bba..c4818c611 100644 --- a/trezorlib/coins.py +++ b/trezorlib/coins.py @@ -17,7 +17,7 @@ import os.path import json -from .tx_api import TxApiInsight, TxApiBlockCypher +from .tx_api import TxApiInsight COINS_JSON = os.path.join(os.path.dirname(__file__), 'coins.json') @@ -35,11 +35,11 @@ def _load_coins_json(): def _insight_for_coin(coin): - if not coin['bitcore']: + url = next(iter(coin['blockbook'] + coin['bitcore']), None) + if not url: return None zcash = coin['coin_name'].lower().startswith('zcash') network = 'insight_{}'.format(coin['coin_name'].lower().replace(' ', '_')) - url = coin['bitcore'][0] + '/api/' return TxApiInsight(network=network, url=url, zcash=zcash) @@ -54,7 +54,4 @@ except Exception as e: slip44 = {name: coin['slip44'] for name, coin in by_name.items()} tx_api = {name: _insight_for_coin(coin) for name, coin in by_name.items() - if coin["bitcore"]} - -# fixup for Dogecoin -tx_api['Dogecoin'] = TxApiBlockCypher(network='blockcypher_dogecoin', url='https://api.blockcypher.com/v1/doge/main/') + if coin["blockbook"] or coin["bitcore"]} diff --git a/trezorlib/tests/device_tests/test_msg_signtx.py b/trezorlib/tests/device_tests/test_msg_signtx.py index 6ce86a6ba..0ea9529fb 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx.py +++ b/trezorlib/tests/device_tests/test_msg_signtx.py @@ -20,12 +20,12 @@ import pytest from .common import TrezorTest from .conftest import TREZOR_VERSION -from trezorlib import coins from trezorlib import messages as proto from trezorlib.client import CallException from trezorlib.tools import parse_path +from trezorlib.tx_api import TxApiInsight -TxApiTestnet = coins.tx_api['Testnet'] +TxApiTestnet = TxApiInsight("insight_testnet") TXHASH_157041 = unhexlify('1570416eb4302cf52979afd5e6909e37d8fdd874301f7cc87e547e509cb1caa6') diff --git a/trezorlib/tests/device_tests/test_msg_signtx_segwit.py b/trezorlib/tests/device_tests/test_msg_signtx_segwit.py index 28b46dcff..f20d49cb2 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_segwit.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_segwit.py @@ -21,12 +21,12 @@ from ..support.ckd_public import deserialize from .conftest import TREZOR_VERSION from .common import TrezorTest -from trezorlib import coins from trezorlib import messages as proto from trezorlib.client import CallException from trezorlib.tools import parse_path +from trezorlib.tx_api import TxApiInsight -TxApiTestnet = coins.tx_api["Testnet"] +TxApiTestnet = TxApiInsight("insight_testnet") class TestMsgSigntxSegwit(TrezorTest): diff --git a/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py b/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py index c5307463d..4851afb83 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py @@ -18,11 +18,11 @@ from binascii import hexlify, unhexlify from .common import TrezorTest from ..support.ckd_public import deserialize -from trezorlib import coins from trezorlib import messages as proto from trezorlib.tools import parse_path +from trezorlib.tx_api import TxApiInsight -TxApiTestnet = coins.tx_api['Testnet'] +TxApiTestnet = TxApiInsight("insight_testnet") class TestMsgSigntxSegwitNative(TrezorTest): diff --git a/trezorlib/tests/device_tests/test_multisig_change.py b/trezorlib/tests/device_tests/test_multisig_change.py index 6dce36ad9..f41f12165 100644 --- a/trezorlib/tests/device_tests/test_multisig_change.py +++ b/trezorlib/tests/device_tests/test_multisig_change.py @@ -19,15 +19,17 @@ from binascii import hexlify, unhexlify from .common import TrezorTest from ..support import ckd_public as bip32 from trezorlib import messages as proto -from trezorlib.coins import tx_api from trezorlib.tools import parse_path +from trezorlib.tx_api import TxApiInsight + +TxApiTestnet = TxApiInsight("insight_testnet") class TestMultisigChange(TrezorTest): def setup_method(self, method): super(TestMultisigChange, self).setup_method(method) - self.client.set_tx_api(tx_api['Testnet']) + self.client.set_tx_api(TxApiTestnet) node_ext1 = bip32.deserialize('tpubDADHV9u9Y6gkggintTdMjJE3be58zKNLhpxBQyuEM6Pwx3sN9JVLmMCMN4DNVwL9AKec27z5TaWcWuHzMXiGAtcra5DjwWbvppGX4gaEGVN') # m/1 => 02c0d0c5fee952620757c6128dbf327c996cd72ed3358d15d6518a1186099bc15e diff --git a/trezorlib/tests/unit_tests/test_tx_api.py b/trezorlib/tests/unit_tests/test_tx_api.py index 9a12f5666..f67caea44 100644 --- a/trezorlib/tests/unit_tests/test_tx_api.py +++ b/trezorlib/tests/unit_tests/test_tx_api.py @@ -20,7 +20,7 @@ from trezorlib import coins from trezorlib import tx_api TxApiBitcoin = coins.tx_api['Bitcoin'] -TxApiTestnet = coins.tx_api['Testnet'] +TxApiTestnet = tx_api.TxApiInsight("insight_testnet") tests_dir = os.path.dirname(os.path.abspath(__file__)) diff --git a/trezorlib/tx_api.py b/trezorlib/tx_api.py index d407c98bc..28bf37e43 100644 --- a/trezorlib/tx_api.py +++ b/trezorlib/tx_api.py @@ -25,10 +25,9 @@ cache_dir = None class TxApi(object): - def __init__(self, network, url): + def __init__(self, network, url=None): self.network = network self.url = url - self.pushtx_url = url def get_url(self, resource, resourceid): url = '%s%s/%s' % (self.url, resource, resourceid) @@ -44,6 +43,9 @@ class TxApi(object): except: pass + if not self.url: + raise RuntimeError("No URL specified and tx not in cache") + try: url = self.get_url(resource, resourceid) r = requests.get(url, headers={'User-agent': 'Mozilla/5.0'}) @@ -63,10 +65,11 @@ class TxApi(object): class TxApiInsight(TxApi): - def __init__(self, network, url, zcash=None): - super(TxApiInsight, self).__init__(network, url) + def __init__(self, network, url=None, zcash=None): + super().__init__(network, url) self.zcash = zcash - self.pushtx_url = url.replace('/api/', '/tx/send') + if url: + self.pushtx_url = url.replace('/api/', '/tx/send') def get_tx(self, txhash): @@ -112,38 +115,3 @@ class TxApiInsight(TxApi): t.extra_data = raw[-extra_data_len:] return t - - -class TxApiBlockCypher(TxApi): - - def __init__(self, network, url, zcash=None): - super(TxApiBlockCypher, self).__init__(network, url) - self.pushtx_url = url.replace('//api.', '//live.').replace('/v1/', '/').replace('/main/', '/pushtx/') - - def get_tx(self, txhash): - - data = self.fetch_json('txs', txhash) - - t = proto.TransactionType() - t.version = data['ver'] - t.lock_time = data.get('lock_time', 0) - - for vin in data['inputs']: - i = t._add_inputs() - 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._add_bin_outputs() - o.amount = int(str(vout['value']), 10) - o.script_pubkey = binascii.unhexlify(vout['script']) - - return t