From 1393e9736e9d979603492802f35b1c5567260fe1 Mon Sep 17 00:00:00 2001 From: Cryp Toon Date: Tue, 14 May 2024 19:28:28 +0200 Subject: [PATCH] Remove bitgo and bitaps providers for testnet, reenable blockcypher --- bitcoinlib/data/providers.json | 33 +++------ bitcoinlib/services/bitaps.py | 2 + bitcoinlib/services/bitgo.py | 2 + bitcoinlib/services/blockcypher.py | 65 +++++++++--------- tests/test_wallets.py | 104 ++++++++++++++--------------- 5 files changed, 97 insertions(+), 109 deletions(-) diff --git a/bitcoinlib/data/providers.json b/bitcoinlib/data/providers.json index 40190a30..d7fd2dc2 100644 --- a/bitcoinlib/data/providers.json +++ b/bitcoinlib/data/providers.json @@ -32,17 +32,6 @@ "denominator": 1, "network_overrides": null }, - "bitgo.testnet": { - "provider": "bitgo", - "network": "testnet", - "client_class": "BitGoClient", - "provider_coin_id": "", - "url": "https://test.bitgo.com/api/v1/", - "api_key": "", - "priority": 10, - "denominator": 1, - "network_overrides": null - }, "blockcypher.litecoin": { "provider": "blockcypher", "network": "litecoin", @@ -142,17 +131,6 @@ "denominator": 100000000, "network_overrides": null }, - "bitaps.testnet": { - "provider": "bitaps", - "network": "testnet", - "client_class": "BitapsClient", - "provider_coin_id": "", - "url": "https://api.bitaps.com/btc/testnet/v1/", - "api_key": "", - "priority": 10, - "denominator": 100000000, - "network_overrides": null - }, "bitaps.litecoin": { "provider": "bitaps", "network": "litecoin", @@ -329,6 +307,17 @@ "denominator": 1, "network_overrides": null }, + "blockcypher.testnet": { + "provider": "blockcypher", + "network": "testnet", + "client_class": "BlockCypher", + "provider_coin_id": "", + "url": "https://api.blockcypher.com/v1/btc/test3/", + "api_key": "", + "priority": 10, + "denominator": 1, + "network_overrides": null + }, "mempool": { "provider": "mempool", "network": "bitcoin", diff --git a/bitcoinlib/services/bitaps.py b/bitcoinlib/services/bitaps.py index a31c6aab..29ec78ef 100644 --- a/bitcoinlib/services/bitaps.py +++ b/bitcoinlib/services/bitaps.py @@ -172,6 +172,8 @@ def gettransactions(self, address, after_txid='', limit=MAX_TRANSACTIONS): # def estimatefee def blockcount(self): + if self.network == 'testnet': + raise ClientError('Providers return incorrect blockcount for testnet') return self.compose_request('block', 'last')['data']['height'] # def mempool(self, txid): diff --git a/bitcoinlib/services/bitgo.py b/bitcoinlib/services/bitgo.py index 1013b425..eab4c353 100644 --- a/bitcoinlib/services/bitgo.py +++ b/bitcoinlib/services/bitgo.py @@ -166,6 +166,8 @@ def estimatefee(self, blocks): return res['feePerKb'] def blockcount(self): + if self.network == 'testnet': + raise ClientError('Providers return incorrect blockcount for testnet') return self.compose_request('block', 'latest')['height'] # def mempool diff --git a/bitcoinlib/services/blockcypher.py b/bitcoinlib/services/blockcypher.py index 882ec7ad..f313e496 100644 --- a/bitcoinlib/services/blockcypher.py +++ b/bitcoinlib/services/blockcypher.py @@ -56,39 +56,38 @@ def getbalance(self, addresslist): balance += float(rec['final_balance']) return int(balance * self.units) - # Disabled: Invalid results for https://api.blockcypher.com/v1/ltc/main/addrs/LVqLipGhyQ1nWtPPc8Xp3zn6JxcU1Hi8eG?unspentOnly=1&limit=2000 - # def getutxos(self, address, after_txid='', limit=MAX_TRANSACTIONS): - # address = self._address_convert(address) - # res = self.compose_request('addrs', address.address, variables={'unspentOnly': 1, 'limit': 2000}) - # transactions = [] - # if not isinstance(res, list): - # res = [res] - # for a in res: - # txrefs = a.setdefault('txrefs', []) + a.get('unconfirmed_txrefs', []) - # if len(txrefs) > 500: - # _logger.warning("BlockCypher: Large number of transactions for address %s, " - # "Transaction list may be incomplete" % address) - # for tx in txrefs: - # if tx['tx_hash'] == after_txid: - # break - # tdate = None - # if 'confirmed' in tx: - # try: - # tdate = datetime.strptime(tx['confirmed'], "%Y-%m-%dT%H:%M:%SZ") - # except ValueError: - # tdate = datetime.strptime(tx['confirmed'], "%Y-%m-%dT%H:%M:%S.%fZ") - # transactions.append({ - # 'address': address.address_orig, - # 'txid': tx['tx_hash'], - # 'confirmations': tx['confirmations'], - # 'output_n': tx['tx_output_n'], - # 'index': 0, - # 'value': int(round(tx['value'] * self.units, 0)), - # 'script': '', - # 'block_height': None, - # 'date': tdate - # }) - # return transactions[::-1][:limit] + def getutxos(self, address, after_txid='', limit=MAX_TRANSACTIONS): + address = self._address_convert(address) + res = self.compose_request('addrs', address.address, variables={'unspentOnly': 1, 'limit': 2000}) + transactions = [] + if not isinstance(res, list): + res = [res] + for a in res: + txrefs = a.setdefault('txrefs', []) + a.get('unconfirmed_txrefs', []) + if len(txrefs) > 500: + _logger.warning("BlockCypher: Large number of transactions for address %s, " + "Transaction list may be incomplete" % address) + for tx in txrefs: + if tx['tx_hash'] == after_txid: + break + tdate = None + if 'confirmed' in tx: + try: + tdate = datetime.strptime(tx['confirmed'], "%Y-%m-%dT%H:%M:%SZ") + except ValueError: + tdate = datetime.strptime(tx['confirmed'], "%Y-%m-%dT%H:%M:%S.%fZ") + transactions.append({ + 'address': address.address_orig, + 'txid': tx['tx_hash'], + 'confirmations': tx['confirmations'], + 'output_n': tx['tx_output_n'], + 'index': 0, + 'value': int(round(tx['value'] * self.units, 0)), + 'script': '', + 'block_height': None, + 'date': tdate + }) + return transactions[::-1][:limit] def gettransaction(self, txid): tx = self.compose_request('txs', txid, variables={'includeHex': 'true'}) diff --git a/tests/test_wallets.py b/tests/test_wallets.py index 31b613e0..1730acb6 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -1408,7 +1408,7 @@ def test_wallet_multisig_info(self): HDKey(network='bitcoinlib_test')], network='bitcoinlib_test', cosigner_id=0, db_uri=self.database_uri) w.utxos_update() - w.info(detail=6) + self.assertIsNone(w.info(detail=6)) def test_wallets_multisig_missing_private_and_cosigner(self): k0 = 'xprv9s21ZrQH143K459uwGGCU3Wj3v1LFFJ42tgyTsNnr6p2BS6FZ9jQ7fmZMMnqsWSi2BBgpX3hFbR4ode8Jx58ibSNeaBLFQ68Xs3' \ @@ -1729,18 +1729,16 @@ def test_wallet_offline_create_transaction(self): del wlt def test_wallet_scan(self): - # TODO: Fix MySQL scan errors - if self.database_uri.startswith('mysql'): - self.skipTest('TODO: Fix MySQL scan errors') account_key = 'tpubDCmJWqxWch7LYDhSuE1jEJMbAkbkDm3DotWKZ69oZfNMzuw7U5DwEaTVZHGPzt5j9BJDoxqVkPHt2EpUF66FrZhpfq' \ 'ZY6DFj6x61Wwbrg8Q' wallet = wallet_create_or_open('scan-test', keys=account_key, network='testnet', db_uri=self.database_uri) - wallet.scan(scan_gap_limit=8) - self.assertEqual(len(wallet.keys()), 27) - self.assertEqual(wallet.balance(), 60500000) - self.assertEqual(len(wallet.transactions()), 4) - self.assertEqual(len(wallet.transactions(as_dict=True)), 4) + wallet.scan(scan_gap_limit=1) + self.assertEqual(len(wallet.keys()), 6) + self.assertEqual(wallet.balance(), 60000000) + self.assertEqual(len(wallet.transactions()), 3) + self.assertEqual(len(wallet.transactions(as_dict=True)), 3) + def test_wallet_scan_tx_order_same_block(self): # Check tx order in same block address = 'tb1qlh9x3jwhfqspp7u9w6l7zqxpmuvplzaczaele3' w = wallet_create_or_open('fix-multiple-tx-1-block', keys=address, db_uri=self.database_uri) @@ -2012,33 +2010,33 @@ def test_wallet_transaction_sign_with_wif(self): self.assertIsNone(t.send()) self.assertTrue(t.pushed) - #FIXME - # def test_wallet_transaction_restore_saved_tx(self): - # w = wallet_create_or_open('test_wallet_transaction_restore', network='bitcoinlib_test', - # db_uri=self.database_uri) - # wk = w.get_key() - # if not USE_FASTECDSA: - # self.skipTest("Need fastecdsa module with deterministic txid's to run this test") - # utxos = [{ - # 'address': wk.address, - # 'script': '', - # 'confirmations': 10, - # 'output_n': 1, - # 'txid': '9f5d4004c7cc5a31a735bddea6ff517e52f1cd700df208d2c39ddc536670f1fe', - # 'value': 1956783097 - # }] - # w.utxos_update(utxos=utxos) - # to = w.get_key_change() - # t = w.sweep(to.address) - # tx_id = t.store() - # wallet_empty('test_wallet_transaction_restore', db_uri=self.database_uri) - # w = wallet_create_or_open('test_wallet_transaction_restore', network='bitcoinlib_test', - # db_uri=self.database_uri) - # w.get_key() - # w.utxos_update(utxos=utxos) - # to = w.get_key_change() - # t = w.sweep(to.address) - # self.assertEqual(t.store(), tx_id) + def test_wallet_transaction_restore_saved_tx(self): + w = wallet_create_or_open('test_wallet_transaction_restore', network='bitcoinlib_test', + db_uri=self.database_uri) + wk = w.get_key() + if not USE_FASTECDSA: + self.skipTest("Need fastecdsa module with deterministic txid's to run this test") + utxos = [{ + 'address': wk.address, + 'script': '', + 'confirmations': 10, + 'output_n': 1, + 'txid': '9f5d4004c7cc5a31a735bddea6ff517e52f1cd700df208d2c39ddc536670f1fe', + 'value': 1956783097 + }] + w.utxos_update(utxos=utxos) + to = w.get_key_change() + t = w.sweep(to.address) + tx_id = t.store() + del w + wallet_empty('test_wallet_transaction_restore', db_uri=self.database_uri) + w = wallet_create_or_open('test_wallet_transaction_restore', network='bitcoinlib_test', + db_uri=self.database_uri) + w.get_key() + w.utxos_update(utxos=utxos) + to = w.get_key_change() + t = w.sweep(to.address) + self.assertEqual(t.store(), tx_id) def test_wallet_transaction_send_keyid(self): w = Wallet.create('wallet_send_key_id', witness_type='segwit', network='bitcoinlib_test', @@ -2362,24 +2360,22 @@ def test_wallet_transaction_replace_by_fee(self): self.assertTrue(t2.replace_by_fee) self.assertEqual(t2.inputs[0].sequence, SEQUENCE_REPLACE_BY_FEE) - # fiXME - # def test_wallet_anti_fee_sniping(self): - # w = wallet_create_or_open('antifeesnipingtestwallet', network='testnet', db_uri=self.database_uri) - # w.utxo_add(w.get_key().address, 1234567, os.urandom(32).hex(), 1) - # t = w.send_to('tb1qrjtz22q59e76mhumy0p586cqukatw5vcd0xvvz', 123456) - # # FIXME: Bitaps and Bitgo return incorrect blockcount for testnet - # block_height = Service(network='testnet', exclude_providers=['bitgo', 'bitaps'], cache_uri='').blockcount() - # self.assertAlmostEqual(t.locktime, block_height+1, delta=3) - # - # w2 = wallet_create_or_open('antifeesnipingtestwallet2', network='testnet', anti_fee_sniping=True) - # w2.utxo_add(w2.get_key().address, 1234567, os.urandom(32).hex(), 1) - # t = w2.send_to('tb1qrjtz22q59e76mhumy0p586cqukatw5vcd0xvvz', 123456, locktime=1901070183) - # self.assertEqual(t.locktime, 1901070183) - # - # w3 = wallet_create_or_open('antifeesnipingtestwallet3', network='testnet', anti_fee_sniping=False) - # w3.utxo_add(w3.get_key().address, 1234567, os.urandom(32).hex(), 1) - # t = w3.send_to('tb1qrjtz22q59e76mhumy0p586cqukatw5vcd0xvvz', 123456) - # self.assertEqual(t.locktime, 0) + def test_wallet_anti_fee_sniping(self): + w = wallet_create_or_open('antifeesnipingtestwallet', network='testnet', db_uri=self.database_uri) + w.utxo_add(w.get_key().address, 1234567, os.urandom(32).hex(), 1) + t = w.send_to('tb1qrjtz22q59e76mhumy0p586cqukatw5vcd0xvvz', 123456) + block_height = Service(network='testnet', cache_uri='').blockcount() + self.assertAlmostEqual(t.locktime, block_height+1, delta=3) + + w2 = wallet_create_or_open('antifeesnipingtestwallet2', network='testnet', anti_fee_sniping=True) + w2.utxo_add(w2.get_key().address, 1234567, os.urandom(32).hex(), 1) + t = w2.send_to('tb1qrjtz22q59e76mhumy0p586cqukatw5vcd0xvvz', 123456, locktime=1901070183) + self.assertEqual(t.locktime, 1901070183) + + w3 = wallet_create_or_open('antifeesnipingtestwallet3', network='testnet', anti_fee_sniping=False) + w3.utxo_add(w3.get_key().address, 1234567, os.urandom(32).hex(), 1) + t = w3.send_to('tb1qrjtz22q59e76mhumy0p586cqukatw5vcd0xvvz', 123456) + self.assertEqual(t.locktime, 0) @classmethod def tearDownClass(cls):