From b3fae42d69ff5d906fe863371f6932a781955b63 Mon Sep 17 00:00:00 2001 From: frier17 Date: Thu, 27 Jul 2023 23:28:27 +0100 Subject: [PATCH] updated utils.py and tests for using Decimal instead of float --- .gitignore | 1 + blockcypher/utils.py | 96 ++++++---- test_blockcypher.py | 421 +++++++++++++++++++++++-------------------- 3 files changed, 280 insertions(+), 238 deletions(-) diff --git a/.gitignore b/.gitignore index fcd9d9b..926a844 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ coverage.xml # Django stuff: *.log *.pot +.idea/ \ No newline at end of file diff --git a/blockcypher/utils.py b/blockcypher/utils.py index 8cada9a..616d9f5 100644 --- a/blockcypher/utils.py +++ b/blockcypher/utils.py @@ -1,12 +1,13 @@ import re - -from .constants import SHA_COINS, SCRYPT_COINS, ETHASH_COINS, COIN_SYMBOL_SET, COIN_SYMBOL_MAPPINGS, FIRST4_MKEY_CS_MAPPINGS_UPPER, UNIT_CHOICES, UNIT_MAPPINGS -from .crypto import script_to_address +from collections import OrderedDict +from decimal import Decimal +from hashlib import sha256 from bitcoin import safe_from_hex, deserialize -from collections import OrderedDict -from hashlib import sha256 +from .constants import SHA_COINS, SCRYPT_COINS, ETHASH_COINS, COIN_SYMBOL_SET, COIN_SYMBOL_MAPPINGS, \ + FIRST4_MKEY_CS_MAPPINGS_UPPER, UNIT_CHOICES, UNIT_MAPPINGS +from .crypto import script_to_address HEX_CHARS_RE = re.compile('^[0-9a-f]*$') @@ -32,11 +33,21 @@ def to_base_unit(input_quantity, input_type): ''' convert to satoshis or wei, no rounding ''' assert input_type in UNIT_CHOICES, input_type + if isinstance(input_quantity, (int, float, Decimal)): + input_quantity = Decimal(input_quantity) + elif isinstance(input_quantity, str): + if re.match(r'^[+-]?(?:\d*\.)?\d+$', input_quantity): + input_quantity = Decimal(input_quantity) + else: + raise TypeError('Provided value (%s) cannot be parsed to numerical type %s' % input_quantity) + else: + raise TypeError('Expected quantity to be data of type int, float, or Decimal but got %s' % type(input_quantity)) + # convert to satoshis if input_type in ('btc', 'mbtc', 'bit'): - base_unit = float(input_quantity) * float(UNIT_MAPPINGS[input_type]['satoshis_per']) + base_unit = input_quantity * Decimal(UNIT_MAPPINGS[input_type]['satoshis_per']) elif input_type in ('ether', 'gwei'): - base_unit = float(input_quantity) * float(UNIT_MAPPINGS[input_type]['wei_per']) + base_unit = input_quantity * Decimal(UNIT_MAPPINGS[input_type]['wei_per']) elif input_type in ['satoshi', 'wei']: base_unit = input_quantity else: @@ -48,11 +59,11 @@ def to_base_unit(input_quantity, input_type): def from_base_unit(input_base, output_type): # convert to output_type, if output_type in ('btc', 'mbtc', 'bit'): - return input_base / float(UNIT_MAPPINGS[output_type]['satoshis_per']) + return Decimal(input_base) / Decimal(UNIT_MAPPINGS[output_type]['satoshis_per']) elif output_type in ('ether', 'gwei'): - return input_base / float(UNIT_MAPPINGS[output_type]['wei_per']) + return Decimal(input_base) / Decimal(UNIT_MAPPINGS[output_type]['wei_per']) elif output_type in ['satoshi', 'wei']: - return int(input_base) + return Decimal(input_base) else: raise Exception('Invalid Unit Choice: %s' % output_type) @@ -60,6 +71,7 @@ def from_base_unit(input_base, output_type): def satoshis_to_btc(satoshis): return from_base_unit(input_base=satoshis, output_type='btc') + def wei_to_ether(wei): return from_base_unit(input_base=wei, output_type='ether') @@ -102,7 +114,8 @@ def safe_trim(qty_as_string): return qty_formatted -def format_crypto_units(input_quantity, input_type, output_type, coin_symbol=None, print_cs=False, safe_trimming=False, round_digits=0): +def format_crypto_units(input_quantity, input_type, output_type, coin_symbol=None, print_cs=False, safe_trimming=False, + round_digits=0): ''' Take an input like 11002343 satoshis and convert it to another unit (e.g. BTC) and format it with appropriate units @@ -126,12 +139,12 @@ def format_crypto_units(input_quantity, input_type, output_type, coin_symbol=Non base_unit_float = to_base_unit(input_quantity=input_quantity, input_type=input_type) if round_digits: - base_unit_float = round(base_unit_float, -1*round_digits) + base_unit_float = round(base_unit_float, -1 * round_digits) output_quantity = from_base_unit( - input_base=base_unit_float, - output_type=output_type, - ) + input_base=base_unit_float, + output_type=output_type, + ) if output_type == 'bit' and round_digits >= 2: pass @@ -146,9 +159,9 @@ def format_crypto_units(input_quantity, input_type, output_type, coin_symbol=Non if print_cs: curr_symbol = get_curr_symbol( - coin_symbol=coin_symbol, - output_type=output_type, - ) + coin_symbol=coin_symbol, + output_type=output_type, + ) output_quantity_formatted += ' %s' % curr_symbol return output_quantity_formatted @@ -195,9 +208,9 @@ def get_txn_outputs(raw_tx_hex, output_addr_list, coin_symbol): # determine if the address is a pubkey address, script address, or op_return pubkey_addr = script_to_address(out['script'], - vbyte=COIN_SYMBOL_MAPPINGS[coin_symbol]['vbyte_pubkey']) + vbyte=COIN_SYMBOL_MAPPINGS[coin_symbol]['vbyte_pubkey']) script_addr = script_to_address(out['script'], - vbyte=COIN_SYMBOL_MAPPINGS[coin_symbol]['vbyte_script']) + vbyte=COIN_SYMBOL_MAPPINGS[coin_symbol]['vbyte_script']) nulldata = out['script'] if out['script'][0:2] == '6a' else None if pubkey_addr in output_addr_set: address = pubkey_addr @@ -212,7 +225,7 @@ def get_txn_outputs(raw_tx_hex, output_addr_list, coin_symbol): raise Exception('Script %s Does Not Contain a Valid Output Address: %s' % ( out['script'], output_addr_set, - )) + )) outputs.append(output) return outputs @@ -238,12 +251,12 @@ def compress_txn_outputs(txn_outputs): def get_txn_outputs_dict(raw_tx_hex, output_addr_list, coin_symbol): return compress_txn_outputs( - txn_outputs=get_txn_outputs( - raw_tx_hex=raw_tx_hex, - output_addr_list=output_addr_list, - coin_symbol=coin_symbol, - ) - ) + txn_outputs=get_txn_outputs( + raw_tx_hex=raw_tx_hex, + output_addr_list=output_addr_list, + coin_symbol=coin_symbol, + ) + ) def compress_txn_inputs(txn_inputs): @@ -310,7 +323,7 @@ def is_valid_wallet_name(wallet_name): def btc_to_satoshis(btc): - return int(float(btc) * UNIT_MAPPINGS['btc']['satoshis_per']) + return int(Decimal(btc) * Decimal(UNIT_MAPPINGS['btc']['satoshis_per'])) def uses_only_hash_chars(string): @@ -348,14 +361,14 @@ def flatten_txns_by_hash(tx_list, nesting=True): else: nested_cleaned_txs[tx_hash] = { - 'txns_satoshis_list': [satoshis, ], - 'satoshis_net': satoshis, - 'received_at': tx.get('received'), - 'confirmed_at': tx.get('confirmed'), - 'confirmations': tx.get('confirmations', 0), - 'block_height': tx.get('block_height'), - 'double_spend': tx.get('double_spend', False), - } + 'txns_satoshis_list': [satoshis, ], + 'satoshis_net': satoshis, + 'received_at': tx.get('received'), + 'confirmed_at': tx.get('confirmed'), + 'confirmations': tx.get('confirmations', 0), + 'block_height': tx.get('block_height'), + 'double_spend': tx.get('double_spend', False), + } if nesting: return nested_cleaned_txs else: @@ -377,7 +390,7 @@ def is_valid_block_num(block_num): return False # hackey approximation - return 0 <= bn_as_int <= 10**9 + return 0 <= bn_as_int <= 10 ** 9 def is_valid_sha_block_hash(block_hash): @@ -388,6 +401,7 @@ def is_valid_scrypt_block_hash(block_hash): " Unfortunately this is indistiguishable from a regular hash " return is_valid_hash(block_hash) + def is_valid_ethash_block_hash(block_hash): " Unfortunately this is indistiguishable from a regular hash " return is_valid_hash(block_hash) @@ -400,9 +414,11 @@ def is_valid_sha_block_representation(block_representation): def is_valid_scrypt_block_representation(block_representation): return is_valid_block_num(block_representation) or is_valid_scrypt_block_hash(block_representation) + def is_valid_ethash_block_representation(block_representation): return is_valid_block_num(block_representation) or is_valid_ethash_block_hash(block_representation) + def is_valid_bcy_block_representation(block_representation): block_representation = str(block_representation) # TODO: more specific rules @@ -447,6 +463,7 @@ def coin_symbol_from_mkey(mkey): ''' return FIRST4_MKEY_CS_MAPPINGS_UPPER.get(mkey[:4].upper()) + # Addresses # # Copied 2014-09-24 from http://rosettacode.org/wiki/Bitcoin/address_validation#Python @@ -485,7 +502,8 @@ def crypto_address_valid(bc): def is_valid_address(b58_address): # TODO deeper validation of a bech32 address - if b58_address.startswith('bc1') or b58_address.startswith('ltc1') or b58_address.startswith('tltc1') or b58_address.startswith('tb1'): + if b58_address.startswith('bc1') or b58_address.startswith('ltc1') or b58_address.startswith( + 'tltc1') or b58_address.startswith('tb1'): return True try: @@ -494,6 +512,7 @@ def is_valid_address(b58_address): # handle edge cases like an address too long to decode return False + def is_valid_eth_address(addr): if addr.startswith('0x'): addr = addr[2:].strip() @@ -503,6 +522,7 @@ def is_valid_eth_address(addr): return uses_only_hash_chars(addr) + def is_valid_address_for_coinsymbol(b58_address, coin_symbol): ''' Is an address both valid *and* start with the correct character diff --git a/test_blockcypher.py b/test_blockcypher.py index f9d8aac..120da07 100644 --- a/test_blockcypher.py +++ b/test_blockcypher.py @@ -1,18 +1,14 @@ +import os import unittest -from blockcypher.utils import is_valid_hash - -from blockcypher import simple_spend, simple_spend_p2sh -from blockcypher import get_broadcast_transactions, get_transaction_details -from blockcypher import get_address_details, get_addresses_details -from blockcypher import list_wallet_names from blockcypher import create_unsigned_tx, create_hd_wallet, derive_hd_address, delete_wallet from blockcypher import generate_new_address, generate_multisig_address - -from blockcypher.utils import is_valid_address, uses_only_hash_chars - -import os - +from blockcypher import get_address_details, get_addresses_details +from blockcypher import get_broadcast_transactions, get_transaction_details +from blockcypher import list_wallet_names +from blockcypher import simple_spend, simple_spend_p2sh +from blockcypher.utils import is_valid_address, uses_only_hash_chars, to_base_unit, from_base_unit, format_crypto_units +from blockcypher.utils import is_valid_hash BC_API_KEY = os.getenv('BC_API_KEY') assert BC_API_KEY, 'Blockcypher API KEY Required for Unit Tests' @@ -31,6 +27,26 @@ def test_valid_hash(self): def test_invalid_hash(self): assert not is_valid_hash(self.invalid_hash), self.invalid_hash + def test_to_base_unit(self): + a = to_base_unit('0.4578', 'btc') + b = to_base_unit('457.80', 'mbtc') + assert a == b + + def test_from_base_unit(self): + a = from_base_unit(12178001, 'mbtc') + b = from_base_unit(12178001, 'btc') + print(f'mBTC: {a}') + print(f'BTC: {b}') + assert a + assert b + + def test_format_crypto_units(self): + a = format_crypto_units(123, 'mbtc', 'btc') + b = format_crypto_units(123, 'mbtc', 'btc', coin_symbol='btc', print_cs=True) + print(f'Formatted output: {a}') + print(f'Formatted output with symbol: {b}') + assert a + class GetAddressesDetails(unittest.TestCase): @@ -39,18 +55,18 @@ def setUp(self): def test_get_addresses_details(self): addresses_details = get_addresses_details( - address_list=[ - # 2 of the first used BTC addresses - '1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1', - '1FvzCLoTPGANNjWoUo6jUGuAG3wg1w4YjR', - ], - coin_symbol='btc', - txn_limit=None, - api_key=BC_API_KEY, - # This way the test result never changes: - before_bh=4, - include_script=True, - ) + address_list=[ + # 2 of the first used BTC addresses + '1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1', + '1FvzCLoTPGANNjWoUo6jUGuAG3wg1w4YjR', + ], + coin_symbol='btc', + txn_limit=None, + api_key=BC_API_KEY, + # This way the test result never changes: + before_bh=4, + include_script=True, + ) assert len(addresses_details) == 2 @@ -58,14 +74,16 @@ def test_get_addresses_details(self): address = addr_obj.get('address') if address == '1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1': assert len(addr_obj['txrefs']) == 1 - assert addr_obj['txrefs'][0]['tx_hash'] == '9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5' + assert addr_obj['txrefs'][0][ + 'tx_hash'] == '9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5' assert addr_obj['txrefs'][0]['block_height'] == 2 assert addr_obj['txrefs'][0]['confirmed'] is not None assert addr_obj['txrefs'][0]['tx_input_n'] == -1 assert addr_obj['txrefs'][0]['tx_output_n'] == 0 elif address == '1FvzCLoTPGANNjWoUo6jUGuAG3wg1w4YjR': assert len(addresses_details[1]['txrefs']) == 1 - assert addr_obj['txrefs'][0]['tx_hash'] == '999e1c837c76a1b7fbb7e57baf87b309960f5ffefbf2a9b95dd890602272f644' + assert addr_obj['txrefs'][0][ + 'tx_hash'] == '999e1c837c76a1b7fbb7e57baf87b309960f5ffefbf2a9b95dd890602272f644' assert addr_obj['txrefs'][0]['block_height'] == 3 assert addr_obj['txrefs'][0]['confirmed'] is not None assert addr_obj['txrefs'][0]['tx_input_n'] == -1 @@ -84,74 +102,74 @@ def setUp(self): def test_create_basic_unsigned(self): # This address I previously sent funds to but threw out the private key result = create_unsigned_tx( - inputs=[ - {'address': 'BwvSPyMWVL1gkp5FZdrGXLpHj2ZJyJYLVB'}, - ], - outputs=[ - { - 'value': -1, - # p2sh address for extra measure - 'address': 'Dbc9fnf1Kqct7zvfNTiwr6HjvDfPYaFSNg', - }, - ], - change_address=None, - include_tosigntx=True, - # will test signature returned locally: - verify_tosigntx=True, - coin_symbol='bcy', - api_key=BC_API_KEY, - ) + inputs=[ + {'address': 'BwvSPyMWVL1gkp5FZdrGXLpHj2ZJyJYLVB'}, + ], + outputs=[ + { + 'value': -1, + # p2sh address for extra measure + 'address': 'Dbc9fnf1Kqct7zvfNTiwr6HjvDfPYaFSNg', + }, + ], + change_address=None, + include_tosigntx=True, + # will test signature returned locally: + verify_tosigntx=True, + coin_symbol='bcy', + api_key=BC_API_KEY, + ) self.assertNotIn('errors', result) def test_create_ps2h_unsigned(self): # This address I previously sent funds to but threw out the private key result = create_unsigned_tx( - inputs=[ - { - 'pubkeys': [ - '036f5ca449944655b5c580ff6686bdd19123d1003b41f49f4b603f53e33f70a2d1', - '03e93a754aa03dedbe032e5be051bce031db4337c48fbbcf970d1b27bb25a07964', - '02582061ab1dba9d6b5b4e6e29f9da2bd590862f1b1e8566f405eb1d92898eafee', - ], - 'script_type': 'multisig-2-of-3' - }, - ], - outputs=[ - { - 'value': -1, - 'address': 'CFr99841LyMkyX5ZTGepY58rjXJhyNGXHf', - }, + inputs=[ + { + 'pubkeys': [ + '036f5ca449944655b5c580ff6686bdd19123d1003b41f49f4b603f53e33f70a2d1', + '03e93a754aa03dedbe032e5be051bce031db4337c48fbbcf970d1b27bb25a07964', + '02582061ab1dba9d6b5b4e6e29f9da2bd590862f1b1e8566f405eb1d92898eafee', ], - change_address=None, - include_tosigntx=True, - # will test signature returned locally: - verify_tosigntx=True, - coin_symbol='bcy', - api_key=BC_API_KEY, - ) + 'script_type': 'multisig-2-of-3' + }, + ], + outputs=[ + { + 'value': -1, + 'address': 'CFr99841LyMkyX5ZTGepY58rjXJhyNGXHf', + }, + ], + change_address=None, + include_tosigntx=True, + # will test signature returned locally: + verify_tosigntx=True, + coin_symbol='bcy', + api_key=BC_API_KEY, + ) self.assertNotIn('errors', result) def test_create_nulldata_unsigned(self): # This address I previously sent funds to but threw out the private key result = create_unsigned_tx( - inputs=[ - {'address': 'BwvSPyMWVL1gkp5FZdrGXLpHj2ZJyJYLVB'}, - ], - outputs=[ - # embed some null-data - { - 'value': 0, - 'script_type': 'null-data', - 'script': '6a06010203040506', - }, - ], - change_address='CFr99841LyMkyX5ZTGepY58rjXJhyNGXHf', - include_tosigntx=True, - # will test signature returned locally: - verify_tosigntx=True, - coin_symbol='bcy', - api_key=BC_API_KEY, - ) + inputs=[ + {'address': 'BwvSPyMWVL1gkp5FZdrGXLpHj2ZJyJYLVB'}, + ], + outputs=[ + # embed some null-data + { + 'value': 0, + 'script_type': 'null-data', + 'script': '6a06010203040506', + }, + ], + change_address='CFr99841LyMkyX5ZTGepY58rjXJhyNGXHf', + include_tosigntx=True, + # will test signature returned locally: + verify_tosigntx=True, + coin_symbol='bcy', + api_key=BC_API_KEY, + ) self.assertNotIn('errors', result) def test_create_from_inputs(self): @@ -199,18 +217,19 @@ class GetAddressDetails(unittest.TestCase): def test_fetching_unspents(self): # This address I previously sent funds to but threw out the private key address_details = get_address_details( - address='C3B3dU12vpCVh2jfmGFdqLe5KWxtZfXW8j', - coin_symbol='bcy', - txn_limit=None, - api_key=BC_API_KEY, - unspent_only=True, - show_confidence=False, # don't return confidence info - # This way the test result never changes: - before_bh=592822, - include_script=True, - ) + address='C3B3dU12vpCVh2jfmGFdqLe5KWxtZfXW8j', + coin_symbol='bcy', + txn_limit=None, + api_key=BC_API_KEY, + unspent_only=True, + show_confidence=False, # don't return confidence info + # This way the test result never changes: + before_bh=592822, + include_script=True, + ) assert len(address_details['txrefs']) == 1 - assert address_details['txrefs'][0]['tx_hash'] == 'b12c4b0ab466c9bbd05da88b3be1a13229c85a6edd2869e01e6a557c8a5cca2b' + assert address_details['txrefs'][0][ + 'tx_hash'] == 'b12c4b0ab466c9bbd05da88b3be1a13229c85a6edd2869e01e6a557c8a5cca2b' assert address_details['txrefs'][0]['block_height'] == 592821 assert address_details['txrefs'][0]['tx_input_n'] == -1 assert address_details['txrefs'][0]['tx_output_n'] == 0 @@ -223,18 +242,19 @@ def test_fetching_unspents(self): def test_get_address_details_before(self): address_details = get_address_details( - address='1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1', - coin_symbol='btc', - txn_limit=None, - api_key=BC_API_KEY, - show_confidence=False, # don't return confidence info - # This way the test result never changes: - before_bh=4, - ) + address='1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1', + coin_symbol='btc', + txn_limit=None, + api_key=BC_API_KEY, + show_confidence=False, # don't return confidence info + # This way the test result never changes: + before_bh=4, + ) # first TX assert len(address_details['txrefs']) == 1 - assert address_details['txrefs'][0]['tx_hash'] == '9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5' + assert address_details['txrefs'][0][ + 'tx_hash'] == '9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5' assert address_details['txrefs'][0]['block_height'] == 2 assert address_details['txrefs'][0]['confirmed'] is not None assert address_details['txrefs'][0]['tx_input_n'] == -1 @@ -242,17 +262,18 @@ def test_get_address_details_before(self): def test_get_address_details_after(self): address_details = get_address_details( - address='1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1', - coin_symbol='btc', - api_key=BC_API_KEY, - show_confidence=False, # don't return confidence info - # Exclude first result - after_bh=4, - txn_limit=1, - ) + address='1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1', + coin_symbol='btc', + api_key=BC_API_KEY, + show_confidence=False, # don't return confidence info + # Exclude first result + after_bh=4, + txn_limit=1, + ) assert len(address_details['txrefs']) == 1 - assert address_details['txrefs'][0]['tx_hash'] != '9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5' + assert address_details['txrefs'][0][ + 'tx_hash'] != '9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5' assert address_details['txrefs'][0]['block_height'] != 2 @@ -261,21 +282,21 @@ class GetUnconfirmedTXInfo(unittest.TestCase): def test_unconfirmed_tx_confidence(self): # fetch a recent tx hash (assume BTC will always have an unconfirmed TX): recent_tx_hash = get_broadcast_transactions( - coin_symbol='btc', - api_key=BC_API_KEY, - limit=1, - )[0]['hash'] + coin_symbol='btc', + api_key=BC_API_KEY, + limit=1, + )[0]['hash'] # get confidence info for it tx_details = get_transaction_details( - tx_hash=recent_tx_hash, - coin_symbol='btc', - limit=1, - tx_input_offset=None, - tx_output_offset=None, - include_hex=False, - confidence_only=True, - api_key=BC_API_KEY, - ) + tx_hash=recent_tx_hash, + coin_symbol='btc', + limit=1, + tx_input_offset=None, + tx_output_offset=None, + include_hex=False, + confidence_only=True, + api_key=BC_API_KEY, + ) assert 'receive_count' in tx_details, tx_details assert 'preference' in tx_details, tx_details @@ -302,19 +323,19 @@ def setUp(self): def test_simple_spend_hex(self): tx_hash = simple_spend( - from_privkey=self.bcy_privkey_hex, - to_address=self.bcy_faucet_addr, - to_satoshis=self.to_send_satoshis, - privkey_is_compressed=True, - api_key=BC_API_KEY, - coin_symbol='bcy', - ) + from_privkey=self.bcy_privkey_hex, + to_address=self.bcy_faucet_addr, + to_satoshis=self.to_send_satoshis, + privkey_is_compressed=True, + api_key=BC_API_KEY, + coin_symbol='bcy', + ) # confirm details (esp that change sent back to sender address) tx_details = get_transaction_details( - tx_hash=tx_hash, - coin_symbol='bcy', - api_key=BC_API_KEY, - ) + tx_hash=tx_hash, + coin_symbol='bcy', + api_key=BC_API_KEY, + ) for input_obj in tx_details['inputs']: assert len(input_obj['addresses']) == 1, input_obj['addresses'] @@ -336,19 +357,19 @@ def test_simple_spend_hex(self): def test_simple_spend_wif(self): tx_hash = simple_spend( - from_privkey=self.bcy_privkey_wif, - to_address=self.bcy_faucet_addr, - to_satoshis=self.to_send_satoshis, - privkey_is_compressed=True, - api_key=BC_API_KEY, - coin_symbol='bcy', - ) + from_privkey=self.bcy_privkey_wif, + to_address=self.bcy_faucet_addr, + to_satoshis=self.to_send_satoshis, + privkey_is_compressed=True, + api_key=BC_API_KEY, + coin_symbol='bcy', + ) # confirm details (esp that change sent back to sender address) tx_details = get_transaction_details( - tx_hash=tx_hash, - coin_symbol='bcy', - api_key=BC_API_KEY, - ) + tx_hash=tx_hash, + coin_symbol='bcy', + api_key=BC_API_KEY, + ) for input_obj in tx_details['inputs']: assert len(input_obj['addresses']) == 1, input_obj['addresses'] @@ -372,33 +393,33 @@ def test_simple_spend_p2sh(self): from_addr = 'Dpuo6iMtoZW3oNsNuALHTEyyw55fBMxiqE' # keys that went into building from_addr all_from_pubkeys = [ - '022d1d33c917e0c1ca677b8c6d47ee55b59880630afe8290517fc7de640ce257f5', - '038a5f1bd7eeb34f53a014f81bfd50869cf6d972ee2bef078f6b67d4c8dd9432b2', - '033796355300f6a50602f701fcf06baebf8b160553e100852703a9363522227a53', - ] + '022d1d33c917e0c1ca677b8c6d47ee55b59880630afe8290517fc7de640ce257f5', + '038a5f1bd7eeb34f53a014f81bfd50869cf6d972ee2bef078f6b67d4c8dd9432b2', + '033796355300f6a50602f701fcf06baebf8b160553e100852703a9363522227a53', + ] # 2 of 3 of the corresponding keys above from_privkeys_to_use = [ - '57067d2852b5f92d18d82a09c2b658184eb85a38fe47adb8db85203a42f91e8f', - 'c4bbc144bc5351288aa46c694a32eceaff739945510cca8bdd924d1c660ff1f4' - ] + '57067d2852b5f92d18d82a09c2b658184eb85a38fe47adb8db85203a42f91e8f', + 'c4bbc144bc5351288aa46c694a32eceaff739945510cca8bdd924d1c660ff1f4' + ] tx_hash = simple_spend_p2sh( - all_from_pubkeys=all_from_pubkeys, - from_privkeys_to_use=from_privkeys_to_use, - to_address=self.bcy_faucet_addr, - to_satoshis=1, - # change addr must be explicit: - change_address=from_addr, - coin_symbol='bcy', - api_key=BC_API_KEY, - ) + all_from_pubkeys=all_from_pubkeys, + from_privkeys_to_use=from_privkeys_to_use, + to_address=self.bcy_faucet_addr, + to_satoshis=1, + # change addr must be explicit: + change_address=from_addr, + coin_symbol='bcy', + api_key=BC_API_KEY, + ) # confirm details (esp that change sent back to sender address) tx_details = get_transaction_details( - tx_hash=tx_hash, - coin_symbol='bcy', - api_key=BC_API_KEY, - ) + tx_hash=tx_hash, + coin_symbol='bcy', + api_key=BC_API_KEY, + ) for input_obj in tx_details['inputs']: assert len(input_obj['addresses']) == 1, input_obj['addresses'] @@ -447,19 +468,19 @@ def setUp(self): def test_simple_spend_hex(self): tx_hash = simple_spend( - from_privkey=self.bcy_privkey_hex, - to_address=self.bcy_faucet_addr, - to_satoshis=self.to_send_satoshis, - privkey_is_compressed=False, - api_key=BC_API_KEY, - coin_symbol='bcy', - ) + from_privkey=self.bcy_privkey_hex, + to_address=self.bcy_faucet_addr, + to_satoshis=self.to_send_satoshis, + privkey_is_compressed=False, + api_key=BC_API_KEY, + coin_symbol='bcy', + ) # confirm details (esp that change sent back to sender address) tx_details = get_transaction_details( - tx_hash=tx_hash, - coin_symbol='bcy', - api_key=BC_API_KEY, - ) + tx_hash=tx_hash, + coin_symbol='bcy', + api_key=BC_API_KEY, + ) for input_obj in tx_details['inputs']: assert len(input_obj['addresses']) == 1, input_obj['addresses'] @@ -481,19 +502,19 @@ def test_simple_spend_hex(self): def test_simple_spend_wif(self): tx_hash = simple_spend( - from_privkey=self.bcy_privkey_wif, - to_address=self.bcy_faucet_addr, - to_satoshis=self.to_send_satoshis, - privkey_is_compressed=False, - api_key=BC_API_KEY, - coin_symbol='bcy', - ) + from_privkey=self.bcy_privkey_wif, + to_address=self.bcy_faucet_addr, + to_satoshis=self.to_send_satoshis, + privkey_is_compressed=False, + api_key=BC_API_KEY, + coin_symbol='bcy', + ) # confirm details (esp that change sent back to sender address) tx_details = get_transaction_details( - tx_hash=tx_hash, - coin_symbol='bcy', - api_key=BC_API_KEY, - ) + tx_hash=tx_hash, + coin_symbol='bcy', + api_key=BC_API_KEY, + ) for input_obj in tx_details['inputs']: assert len(input_obj['addresses']) == 1, input_obj['addresses'] @@ -519,9 +540,9 @@ class GenerateAddressServerSide(unittest.TestCase): def test_generate_single_addr(self): for coin_symbol in ('btc', 'btc-testnet', 'doge', 'dash', 'ltc', 'bcy'): response_dict = generate_new_address( - coin_symbol=coin_symbol, - api_key=BC_API_KEY, - ) + coin_symbol=coin_symbol, + api_key=BC_API_KEY, + ) assert is_valid_address(response_dict['address']), response_dict assert uses_only_hash_chars(response_dict['private']), response_dict assert uses_only_hash_chars(response_dict['public']), response_dict @@ -530,15 +551,15 @@ def test_generate_single_addr(self): def test_generate_multisig_addr(self): # http://www.soroushjp.com/2014/12/20/bitcoin-multisig-the-hard-way-understanding-raw-multisignature-bitcoin-transactions/ response_dict = generate_multisig_address( - pubkey_list=[ - '04a882d414e478039cd5b52a92ffb13dd5e6bd4515497439dffd691a0f12af9575fa349b5694ed3155b136f09e63975a1700c9f4d4df849323dac06cf3bd6458cd', - '046ce31db9bdd543e72fe3039a1f1c047dab87037c36a669ff90e28da1848f640de68c2fe913d363a51154a0c62d7adea1b822d05035077418267b1a1379790187', - '0411ffd36c70776538d079fbae117dc38effafb33304af83ce4894589747aee1ef992f63280567f52f5ba870678b4ab4ff6c8ea600bd217870a8b4f1f09f3a8e83' - ], - script_type='multisig-2-of-3', - coin_symbol='btc', - api_key=BC_API_KEY, - ) + pubkey_list=[ + '04a882d414e478039cd5b52a92ffb13dd5e6bd4515497439dffd691a0f12af9575fa349b5694ed3155b136f09e63975a1700c9f4d4df849323dac06cf3bd6458cd', + '046ce31db9bdd543e72fe3039a1f1c047dab87037c36a669ff90e28da1848f640de68c2fe913d363a51154a0c62d7adea1b822d05035077418267b1a1379790187', + '0411ffd36c70776538d079fbae117dc38effafb33304af83ce4894589747aee1ef992f63280567f52f5ba870678b4ab4ff6c8ea600bd217870a8b4f1f09f3a8e83' + ], + script_type='multisig-2-of-3', + coin_symbol='btc', + api_key=BC_API_KEY, + ) assert response_dict['address'] == '347N1Thc213QqfYCz3PZkjoJpNv5b14kBd', response_dict