diff --git a/bitcoinlib/data/networks.json b/bitcoinlib/data/networks.json index 0948aea9..59ff7305 100644 --- a/bitcoinlib/data/networks.json +++ b/bitcoinlib/data/networks.json @@ -98,6 +98,39 @@ "fee_max": 2000000, "priority": 8 }, + "testnet4": + { + "description": "Bitcoin Test Network 4", + "currency_name": "test-bitcoin", + "currency_name_plural": "test-bitcoins", + "currency_symbol": "TBTC", + "currency_code": "tBTC", + "prefix_address": "6F", + "prefix_address_p2sh": "C4", + "prefix_bech32": "tb", + "prefix_wif": "EF", + "prefixes_wif": [ + ["043587CF", "tpub", "public", false, "legacy", "p2pkh"], + ["043587CF", "tpub", "public", true, "legacy", "p2sh"], + ["04358394", "tprv", "private", false, "legacy", "p2pkh"], + ["04358394", "tprv", "private", true, "legacy", "p2sh"], + ["044A5262", "upub", "public", false, "p2sh-segwit", "p2sh_p2wpkh"], + ["024289EF", "Upub", "public", true, "p2sh-segwit", "p2sh_p2wsh"], + ["044A4E28", "uprv", "private", false, "p2sh-segwit", "p2sh_p2wpkh"], + ["024285B5", "Uprv", "private", true, "p2sh-segwit", "p2sh_p2wsh"], + ["045F1CF6", "vpub", "public", false, "segwit", "p2wpkh"], + ["02575483", "Vpub", "public", true, "segwit", "p2wsh"], + ["045F18BC", "vprv", "private", false, "segwit", "p2wpkh"], + ["02575048", "Vprv", "private", true, "segwit", "p2wsh"] + ], + "bip44_cointype": 1, + "denominator": 0.00000001, + "dust_amount": 1000, + "fee_default": 10000, + "fee_min": 1000, + "fee_max": 2000000, + "priority": 8 + }, "regtest": { "description": "Bitcoin regtest", diff --git a/bitcoinlib/data/providers.json b/bitcoinlib/data/providers.json index 1903d15e..1999f65b 100644 --- a/bitcoinlib/data/providers.json +++ b/bitcoinlib/data/providers.json @@ -359,9 +359,20 @@ "network_overrides": null, "timeout": 0 }, - "mempool.litecoin": { + "mempool.testnet4": { "provider": "mempool", - "network": "litecoin_testnet", + "network": "testnet4", + "client_class": "MempoolClient", + "provider_coin_id": "", + "url": "https://mempool.space/testnet4/api/", + "api_key": "", + "priority": 10, + "denominator": 1, + "network_overrides": null + }, + "mempool.litecoin": { + "provider": "mempool", + "network": "litecoin", "client_class": "MempoolClient", "provider_coin_id": "", "url": "https://litecoinspace.org/api/", diff --git a/bitcoinlib/services/services.py b/bitcoinlib/services/services.py index 7b7e1ac1..a07e45de 100644 --- a/bitcoinlib/services/services.py +++ b/bitcoinlib/services/services.py @@ -113,19 +113,13 @@ def __init__(self, network=DEFAULT_NETWORK, min_providers=1, max_providers=1, pr raise ServiceError("Provider '%s' not found in provider definitions" % p) self.providers = {} - if provider_name: - if provider_name not in self.providers_defined: - raise ServiceError("Provider with name '%s' not found in provider definitions" % provider_name) - if self.providers_defined[provider_name]['network'] != self.network: - raise ServiceError("Network from provider '%s' is different than Service network" % provider_name) - self.providers.update({provider_name: self.providers_defined[provider_name]}) - else: - for p in self.providers_defined: - if (self.providers_defined[p]['network'] == network or self.providers_defined[p]['network'] == '') and \ - (not providers or self.providers_defined[p]['provider'] in providers): - self.providers.update({p: self.providers_defined[p]}) - exclude_providers_keys = {pi: self.providers[pi]['provider'] for - pi in self.providers if self.providers[pi]['provider'] in exclude_providers}.keys() + for p in self.providers_defined: + if ((self.providers_defined[p]['network'] == network or self.providers_defined[p]['network'] == '') and \ + (not providers or self.providers_defined[p]['provider'] in providers) + and self.providers_defined[p]['priority']): + self.providers.update({p: self.providers_defined[p]}) + exclude_providers_keys = {pi: self.providers[pi]['provider'] for pi in self.providers if self.providers[pi]['provider'] in exclude_providers}.keys() + for provider_key in exclude_providers_keys: del(self.providers[provider_key]) diff --git a/bitcoinlib/transactions.py b/bitcoinlib/transactions.py index b709ba08..18706d3f 100644 --- a/bitcoinlib/transactions.py +++ b/bitcoinlib/transactions.py @@ -696,12 +696,12 @@ def __init__(self, value, address='', public_hash=b'', public_key=b'', lock_scri raise TransactionError("Could not determine script type of address %s" % self._address) self.encoding = address_dict['encoding'] network_guesses = address_dict['networks'] - if address_dict['network'] and self.network.name != address_dict['network']: - raise TransactionError("Address %s is from %s network and transaction from %s network" % - (self._address, address_dict['network'], self.network.name)) - elif self.network.name not in network_guesses: + if self.network.name not in network_guesses: raise TransactionError("Network for output address %s is different from transaction network. %s not " "in %s" % (self._address, self.network.name, network_guesses)) + # if address_dict['network'] and self.network.name != address_dict['network']: + # raise TransactionError("Address %s is from %s network and transaction from %s network" % + # (self._address, address_dict['network'], self.network.name)) self.public_hash = address_dict['public_key_hash_bytes'] self.witness_type = address_dict['witness_type'] if not self.encoding: diff --git a/docs/_static/manuals.security.rst b/docs/_static/manuals.security.rst index ac9941f7..fbdf365c 100644 --- a/docs/_static/manuals.security.rst +++ b/docs/_static/manuals.security.rst @@ -4,7 +4,7 @@ Frequently Asked Questions Ten tips for more privacy and security when using Bitcoin and Bitcoinlib: 1. Run your own `Bitcoin core `_, - `Bcoin node `_ node or `Blockbook server `_, so you are not depending on external Blockchain API service providers anymore. + `Bcoin node `_ or `Blockbook server `_, so you are not depending on external Blockchain API service providers anymore. This not only increases your privacy, but also makes your application much faster and more reliable. And as extra bonus you support the Bitcoin network. 2. Use multi-signature wallets. So you are able to store your private keys in separate (offline) locations. diff --git a/tests/test_networks.py b/tests/test_networks.py index d2c385fb..2665fae6 100644 --- a/tests/test_networks.py +++ b/tests/test_networks.py @@ -16,7 +16,7 @@ def test_networks_prefix_wif_network_by_value(self): self.assertEqual(network_by_value('prefix_wif', '10'), []) def test_networks_prefix_bech32_network_by_value(self): - self.assertEqual(network_by_value('prefix_bech32', 'tb'), ['testnet']) + self.assertEqual(network_by_value('prefix_bech32', 'tb'), ['testnet', 'testnet4']) def test_networks_prefix_bech32_network_by_value_sorted(self): self.assertEqual(network_by_value('prefix_bech32', 'ltc'), ['litecoin', 'litecoin_legacy']) diff --git a/tests/test_services.py b/tests/test_services.py index dbaf0113..6a82a033 100644 --- a/tests/test_services.py +++ b/tests/test_services.py @@ -80,6 +80,14 @@ def test_service_transaction_get_raw_testnet(self): '76a914f0d34949650af161e7cb3f0325a1a8833075165088acb7740f00' self.assertEqual(raw_tx, ServiceTest(network='testnet').getrawtransaction(tx_id)) + def test_service_transaction_get_raw_testnet4(self): + tx_id = '619c9db8597dc8aaaa37569e25930efa04c9aef7b604b1b8a26bd4f086b2785c' + raw_tx = ('010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff095100062f4' + '077697a2fffffffff0200f2052a010000001976a9140a59837ccd4df25adc31cdad39be6a8d97557ed688ac0000000000' + '000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000' + '000000000000000000000000000000000000000000000000000000000000000') + self.assertEqual(raw_tx, ServiceTest(network='testnet4').getrawtransaction(tx_id)) + def test_service_transaction_get_raw_bitcoin(self): tx_id = 'b7feea5e7c79d4f6f343b5ca28fa2a1fcacfe9a2b7f44f3d2fd8d6c2d82c4078' raw_tx = '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5d0342fe06244d69' \ @@ -468,6 +476,14 @@ def test_service_gettransactions_litecoin(self): r_inputs[2]['prev_txid'] = 'fa422d9fbac6a344af5656325acde172cd5714ebddd2f35068d3f265095add52' self.assertEqual(r_inputs[0], input0, msg="Unexpected transaction input values for %s provider" % provider) + def test_service_gettransactions_testnet4(self): + address = 'tb1qmtrlx4srl87dtcj73ue5gv39907e8n3qu8rs79' + srv = ServiceTest(network='testnet4') + txs = srv.gettransactions(address) + txids = [tx.txid for tx in txs] + self.assertIn(txs[0].txid, txids) + self.assertIn(txs[1].txid, txids) + def test_service_gettransaction_coinbase(self): expected_dict = { 'block_hash': '0000000000000000002d966c99d68245b20468dc9c2a7a776a836add03362199', @@ -610,7 +626,7 @@ def test_service_network_litecoin_legacy(self): self.assertIn(txid, [utxo['txid'] for utxo in utxos]) def test_service_blockcount(self): - for nw in ['bitcoin', 'litecoin', 'testnet']: + for nw in ['bitcoin', 'litecoin', 'testnet']: # ToDo: add testnet4 when more providers are available srv = ServiceTest(min_providers=3, cache_uri='', network=nw, exclude_providers=['bitgo', 'bitaps']) srv.blockcount() n_blocks = None @@ -802,7 +818,7 @@ def test_service_transaction_unconfirmed(self): self.assertIsNone(t.date) self.assertIsNone(t.block_height) - def test_service_exlude_providers(self): + def test_service_exclude_providers(self): srv = ServiceTest(network='testnet', cache_uri='') providers = [srv.providers[pi]['provider'] for pi in srv.providers] try: diff --git a/tests/test_wallets.py b/tests/test_wallets.py index a95a5ef9..f81d63ef 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -187,6 +187,11 @@ def test_wallet_create_with_passphrase(self): key0 = wlt.get_key() self.assertEqual(key0.address, "mqDeXXaFnWKNWhLmAae7zHhZDW4PMsLHPp") + wlt4 = Wallet.create("wallet-passphrase-testnet4", keys=passphrase, network='testnet4', witness_type='legacy', + db_uri=self.database_uri) + key0 = wlt4.get_key() + self.assertEqual(key0.address, "mqDeXXaFnWKNWhLmAae7zHhZDW4PMsLHPp") + def test_wallet_create_with_passphrase_litecoin(self): passphrase = "always reward element perfect chunk father margin slab pond suffer episode deposit" wlt = Wallet.create("wallet-passphrase-litecoin", keys=passphrase, network='litecoin',